[
  {
    "path": ".clang-format",
    "content": "---\nBasedOnStyle: Google\nAccessModifierOffset: -4\nAllowShortEnumsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortLambdasOnASingleLine: Inline\nBinPackArguments: false\nBinPackParameters: false\nColumnLimit: 120\nIncludeBlocks: Preserve\nIndentWidth: 4\nPackConstructorInitializers: Never\nPenaltyBreakAssignment: 80\nSortIncludes: false\nSpacesBeforeTrailingComments: 1\nStandard: c++20\n---\nLanguage: Cpp\n---\nLanguage: ObjC\nBasedOnStyle: Google\nObjCSpaceAfterProperty: true\n...\n"
  },
  {
    "path": ".cursor/rules/karpathy-guidelines.mdc",
    "content": "---\ndescription: \nalwaysApply: true\n---\n\n# Karpathy behavioral guidelines\n\nBehavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.\n\n**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.\n\n## 1. Think Before Coding\n\n**Don't assume. Don't hide confusion. Surface tradeoffs.**\n\nBefore implementing:\n- State your assumptions explicitly. If uncertain, ask.\n- If multiple interpretations exist, present them - don't pick silently.\n- If a simpler approach exists, say so. Push back when warranted.\n- If something is unclear, stop. Name what's confusing. Ask.\n\n## 2. Simplicity First\n\n**Minimum code that solves the problem. Nothing speculative.**\n\n- No features beyond what was asked.\n- No abstractions for single-use code.\n- No \"flexibility\" or \"configurability\" that wasn't requested.\n- No error handling for impossible scenarios.\n- If you write 200 lines and it could be 50, rewrite it.\n\nAsk yourself: \"Would a senior engineer say this is overcomplicated?\" If yes, simplify.\n\n## 3. Surgical Changes\n\n**Touch only what you must. Clean up only your own mess.**\n\nWhen editing existing code:\n- Don't \"improve\" adjacent code, comments, or formatting.\n- Don't refactor things that aren't broken.\n- Match existing style, even if you'd do it differently.\n- If you notice unrelated dead code, mention it - don't delete it.\n\nWhen your changes create orphans:\n- Remove imports/variables/functions that YOUR changes made unused.\n- Don't remove pre-existing dead code unless asked.\n\nThe test: Every changed line should trace directly to the user's request.\n\n## 4. Goal-Driven Execution\n\n**Define success criteria. Loop until verified.**\n\nTransform tasks into verifiable goals:\n- \"Add validation\" → \"Write tests for invalid inputs, then make them pass\"\n- \"Fix the bug\" → \"Write a test that reproduces it, then make it pass\"\n- \"Refactor X\" → \"Ensure tests pass before and after\"\n\nFor multi-step tasks, state a brief plan:\n```\n1. [Step] → verify: [check]\n2. [Step] → verify: [check]\n3. [Step] → verify: [check]\n```\n\nStrong success criteria let you loop independently. Weak criteria (\"make it work\") require constant clarification.\n\n---\n\n**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: [maplibre]\nopen_collective: maplibre\n"
  },
  {
    "path": ".github/actions/mlt-setup-java/action.yml",
    "content": "name: mlt-setup-java\ndescription: Setup Java requirements\n\ninputs: {}\n\nruns:\n  using: \"composite\"\n  steps:\n\n    - name: Setup Java\n      uses: actions/setup-java@v5\n      with:\n        distribution: 'temurin'\n        java-version: '21'\n\n    - name: Setup Java Gradle\n      uses: gradle/actions/setup-gradle@v5\n"
  },
  {
    "path": ".github/actions/mlt-setup-node/action.yml",
    "content": "name: mlt-setup-node\ndescription: Setup Node-JS requirements\n\ninputs: {}\n\nruns:\n  using: \"composite\"\n  steps:\n\n    - name: Setup Node.js\n      uses: actions/setup-node@v5\n      with:\n        node-version-file: 'ts/.nvmrc'\n        cache: 'npm'\n        cache-dependency-path: 'ts/package-lock.json'\n        registry-url: 'https://registry.npmjs.org'\n"
  },
  {
    "path": ".github/actions/mlt-setup-rust/action.yml",
    "content": "name: mlt-setup-rust\ndescription: Setup Rust requirements\n\ninputs:\n  toolchain:\n    description: 'Rust toolchain to install: stable (default), nightly, or msrv'\n    required: false\n    default: 'stable'\n\nruns:\n  using: \"composite\"\n  steps:\n\n    - uses: taiki-e/install-action@v2\n      with: { tool: 'just,cargo-binstall,cargo-hack,fd-find,cargo-insta,cargo-sort,git-delta' }\n\n    # Install the requested toolchain (stable, nightly, or msrv)\n    - if: ${{ inputs.toolchain == 'stable' }}\n      name: Install Rust stable\n      uses: dtolnay/rust-toolchain@stable\n    - if: ${{ inputs.toolchain == 'nightly' }}\n      name: Install Rust nightly\n      uses: dtolnay/rust-toolchain@nightly\n    - if: ${{ inputs.toolchain == 'msrv' }}\n      name: Read MSRV\n      id: msrv\n      shell: bash\n      run: echo \"value=$(just rust::get-msrv)\" >> $GITHUB_OUTPUT\n    - if: ${{ inputs.toolchain == 'msrv' }}\n      name: Install Rust MSRV ${{ steps.msrv.outputs.value }}\n      uses: dtolnay/rust-toolchain@stable\n      with:\n        toolchain: ${{ steps.msrv.outputs.value }}\n\n    - name: Cache Cargo registry and build artifacts\n      if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'\n      uses: Swatinem/rust-cache@v2\n      with: { workspaces: rust }\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n#\n# Config file spec:\n# https://docs.github.com/en/enterprise-cloud@latest/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem\n\nversion: 2\nupdates:\n\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: github-actions\n    directory: \"/\"\n    schedule:\n      interval: weekly\n    groups:\n      all-actions-version-updates:\n        applies-to: version-updates\n        patterns:\n          - \"*\"\n      all-actions-security-updates:\n        applies-to: security-updates\n        patterns:\n          - \"*\"\n\n  - package-ecosystem: gradle  # should this be \"maven\" or \"gradle\"?\n    directory: \"/java\"\n    schedule:\n      interval: daily\n      time: \"02:00\"\n    groups:\n      all-gradle-version-updates:\n        applies-to: version-updates\n        patterns:\n          - \"*\"\n      all-gradle-security-updates:\n        applies-to: security-updates\n        patterns:\n          - \"*\"\n\n  - package-ecosystem: \"npm\"\n    directory: \"/ts\"\n    schedule:\n      interval: daily\n    versioning-strategy: increase\n    groups:\n      all-npm-version-updates:\n        applies-to: version-updates\n        patterns:\n          - \"*\"\n      all-npm-security-updates:\n        applies-to: security-updates\n        patterns:\n          - \"*\"\n\n  # Update Rust dependencies\n  - package-ecosystem: cargo\n    directory: \"/rust\"\n    schedule:\n      interval: daily\n      time: \"02:00\"\n    open-pull-requests-limit: 10\n    groups:\n      all-cargo-version-updates:\n        applies-to: version-updates\n        patterns:\n          - \"*\"\n      all-cargo-security-updates:\n        applies-to: security-updates\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/autofix.yml",
    "content": "name: autofix.ci # See https://autofix.ci/security why this name\n\non:\n  pull_request:\n    types: [labeled, opened, synchronize, reopened]\n  workflow_dispatch:\n\npermissions: {}\n\njobs:\n  rust-fmt-toml:\n    name: Format Cargo.toml\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    if: |\n      github.event.action == 'opened' ||\n      github.event.action == 'synchronize' ||\n      github.event.action == 'reopened'\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with: { persist-credentials: false }\n      - uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2.62.57\n        with: { tool: 'just,cargo-sort' }\n      - name: Format Cargo.toml\n        run: just rust::fmt-toml\n\n      - uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4\n        with: { commit-message: \"chore: sort Cargo.toml\" }\n  rust-sync-versions:\n    name: Sync Rust-provided bindings version indicators\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    if: |\n      github.event.action == 'opened' ||\n      github.event.action == 'synchronize' ||\n      github.event.action == 'reopened'\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with: { persist-credentials: false }\n      - name: Setup Rust\n        run: rustup update stable && rustup default stable\n      - uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2.62.57\n        with: { tool: 'just' }\n      - run: sudo apt update && sudo apt install -y jq\n      - name: Sync package.json version from Cargo.toml\n        working-directory: rust\n        run: just rust::sync-wasm-version\n      - name: Sync pyproject.toml version from Cargo.toml\n        working-directory: rust\n        run: just rust::sync-pyo3-version\n\n      - uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4\n        with: {commit-message: \"chore: sync secondary version indicators\"}\n\n  rust-gen-pyo3-stubs:\n    name: Regenerate maplibre_tiles.pyi stub\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    if: |\n      github.event.action == 'opened' ||\n      github.event.action == 'synchronize' ||\n      github.event.action == 'reopened'\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with: { persist-credentials: false }\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just' }\n      - uses: ./.github/actions/mlt-setup-rust\n      - name: Regenerate stub\n        run: just rust::sync-pyo3-stubs\n\n      # run pre-commit as a formatting tiebreaker\n      - name: Install uv\n        uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6\n      - run: uvx pre-commit run --all-files\n        continue-on-error: true\n\n      - uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a\n        with: {commit-message: \"chore: regenerate maplibre_tiles.pyi stub\"}\n\n  bless:\n    name: Bless test output\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    if: |\n      github.event.action == 'labeled' &&\n      github.event.label.name == 'bless'\n\n    steps:\n      - name: Checkout sources\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with: { persist-credentials: false }\n      - uses: BRAINSia/free-disk-space@7048ffbf50819342ac964ef3998a51c2564a8a75 # v2.1.3\n        with:\n          tool-cache: false\n          mandb: false\n          android: true\n          dotnet: true\n          haskell: true\n          large-packages: false\n          docker-images: false\n          swap-storage: true\n      - name: Setup Rust\n        run: rustup update stable && rustup default stable\n\n      - name: Install just\n        uses: taiki-e/install-action@cc33365ec7e3350bc47bf935f247582cc6f68344 # v2.65.12\n        with:\n          tool: just,cargo-binstall\n\n      - name: Setup Node.js\n        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0\n\n      - run: just bless\n        continue-on-error: true\n\n      # run pre-commit as a formatting tiebreaker\n      - name: Install uv\n        uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6\n      - run: uvx pre-commit run --all-files\n        continue-on-error: true\n\n      - uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4\n        with: {commit-message: \"chore: update blessed test snapshots across all components\"}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n  push:\n    branches: [main]\n  workflow_dispatch:\n\njobs:\n  changes:\n    runs-on: ubuntu-latest\n    outputs:\n      rust: ${{ steps.filter.outputs.rust }}\n      java: ${{ steps.filter.outputs.java }}\n      ts:   ${{ steps.filter.outputs.ts }}\n      cpp:  ${{ steps.filter.outputs.cpp }}\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: dorny/paths-filter@v4\n        id: filter\n        with:\n          filters: |\n            rust:\n              - 'justfile'\n              - 'rust/mod.just'\n              - '.github/**'\n              - 'rust/**'\n              - 'test/**'\n            java:\n              - 'justfile'\n              - 'java/mod.just'\n              - '.github/**'\n              - 'java/**'\n              - 'test/**'\n            ts:\n              - 'justfile'\n              - 'ts/mod.just'\n              - '.github/**'\n              - 'ts/**'\n              - 'test/**'\n            cpp:\n              - 'justfile'\n              - 'cpp/mod.just'\n              - '.github/**'\n              - 'cpp/**'\n              - 'test/**'\n\n  rust:\n    needs: changes\n    if: needs.changes.outputs.rust == 'true'\n    uses: ./.github/workflows/rust.yml\n    with:\n      run-release: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}\n    secrets: inherit\n    permissions:\n      contents: write\n      id-token: write\n\n  java:\n    needs: changes\n    if: needs.changes.outputs.java == 'true'\n    uses: ./.github/workflows/java.yml\n    permissions:\n      id-token: write\n      contents: read\n\n  ts:\n    needs: changes\n    if: needs.changes.outputs.ts == 'true'\n    uses: ./.github/workflows/ts.yml\n    permissions:\n      contents: read\n\n  cpp:\n    needs: changes\n    if: needs.changes.outputs.cpp == 'true'\n    uses: ./.github/workflows/cpp.yml\n    permissions:\n      id-token: write\n      contents: read\n\n  docs:\n    uses: ./.github/workflows/gh-pages.yml\n    permissions:\n      contents: write\n\n  ci-passed:\n    needs: [rust, java, ts, cpp, docs]\n    if: always()\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo \"${{ toJSON(needs) }}\"\n      - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')\n        run: exit 1\n"
  },
  {
    "path": ".github/workflows/cpp.yml",
    "content": "name: C++ CI\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  cpp-build:\n    permissions:\n      id-token: write  # for OIDC Codecov\n    strategy:\n      matrix:\n        os: [macos-15, ubuntu-24.04]\n    runs-on: ${{ matrix.os }}\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6.0.2\n        with:\n          submodules: recursive\n          fetch-depth: 1\n\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version-file: 'cpp/.nvmrc'\n\n      - name: install gcovr (macOS)\n        if: runner.os == 'macOS'\n        run: brew install gcovr\n\n      - name: install gcovr (Linux)\n        if: runner.os == 'Linux'\n        run: pip3 install gcovr\n\n      - name: Build, test and generate coverage report\n        run: just -v cpp::coverage\n\n      - name: Upload coverage to Codecov\n        if: github.repository_owner == 'maplibre'\n        uses: codecov/codecov-action@v6\n        with:\n          use_oidc: true\n          fail_ci_if_error: true\n          disable_file_fixes: true\n          disable_search: true\n          files: cpp/build/coverage.xml\n\n      - name: Sanity test for mlt-cpp-json tool\n        run: just -v cpp::test-json\n\n      - name: Build with Bazel\n        run: just -v cpp::bazel-build\n\n      - run: just -v assert-git-is-clean\n"
  },
  {
    "path": ".github/workflows/dependabot.yml",
    "content": "name: Dependabot auto-merge\non: pull_request\n\npermissions: write-all\n\njobs:\n  dependabot:\n    runs-on: ubuntu-latest\n    if: github.actor == 'dependabot[bot]'\n    steps:\n      - name: Dependabot metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@v3\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n      - name: Approve Dependabot PRs\n        if: steps.metadata.outputs.update-type == 'version-update:semver-patch'\n        run: gh pr review --approve \"$PR_URL\"\n        env:\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      - name: Enable auto-merge for Dependabot PRs\n        if: steps.metadata.outputs.update-type == 'version-update:semver-patch'\n        run: gh pr merge --auto --squash \"$PR_URL\"\n        env:\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "content": "name: Publish docs\n\non:\n  workflow_call:\n  workflow_dispatch:\n\njobs:\n  gh-pages:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout 🛎️\n        uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v4\n\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - name: Build MLT documentation\n        run: just -v docs-build\n\n      - name: Deploy 🚀\n        if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && github.repository_owner == 'maplibre'\n        uses: JamesIves/github-pages-deploy-action@d92aa235d04922e8f08b40ce78cc5442fcfbfa2f # v4.8.0\n        with: { branch: gh-pages, folder: site }\n\n      - run: just -v assert-git-is-clean\n"
  },
  {
    "path": ".github/workflows/hotpath-comment.yml",
    "content": "name: Rust Hotpath Comment\n\non:\n  workflow_run:\n    workflows: [\"Rust Hotpath Profile\"]\n    types:\n      - completed\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  comment:\n    runs-on: ubuntu-latest\n    if: ${{ github.event.workflow_run.conclusion == 'success' }}\n    steps:\n      - uses: actions/checkout@v6.0.2\n\n      - uses: dtolnay/rust-toolchain@stable\n      - uses: Swatinem/rust-cache@v2\n\n      - uses: actions/download-artifact@v8\n        with:\n          name: profile-metrics\n          path: /tmp/metrics/\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n\n      - name: Install hotpath-utils CLI\n        run: cargo install hotpath --bin hotpath-utils --features=utils\n\n      - name: Post PR comment\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          set -euo pipefail\n          GITHUB_BASE_REF=$(cat /tmp/metrics/base_ref.txt)\n          export GITHUB_BASE_REF\n          GITHUB_HEAD_REF=$(cat /tmp/metrics/head_ref.txt)\n          export GITHUB_HEAD_REF\n          hotpath-utils profile-pr \\\n            --head-metrics /tmp/metrics/head_timing.json \\\n            --base-metrics /tmp/metrics/base_timing.json \\\n            --github-token \"$GH_TOKEN\" \\\n            --pr-number \"$(cat /tmp/metrics/pr_number.txt)\" \\\n            --benchmark-id \"mlt-convert\"\n"
  },
  {
    "path": ".github/workflows/hotpath-profile.yml",
    "content": "name: Rust Hotpath Profile\n\non:\n  pull_request:\n    paths:\n      - \"rust/**\"\n\npermissions:\n  contents: read\n\njobs:\n  profile:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n        with:\n          fetch-depth: 0\n\n      - uses: ./.github/actions/mlt-setup-rust\n\n      - name: Create metrics directory\n        run: mkdir -p /tmp/metrics\n\n      # ── Download benchmark tiles (cached) ──────────────────────────────\n      # 95 fixture tiles are too few for stable hotpath numbers.\n      # See `just rust::download-benchmark-tiles` for details.\n      - name: Cache benchmark tiles\n        id: cache-tiles\n        uses: actions/cache@v5\n        with:\n          path: /tmp/benchmark-tiles\n          key: hotpath-tiles-protomaps-z6-v1\n\n      - name: Download benchmark tiles\n        if: steps.cache-tiles.outputs.cache-hit != 'true'\n        env:\n          GITHUB_TOKEN: ${{ github.token }}\n        run: just rust::download-benchmark-tiles\n\n      - name: Head benchmark (timing + alloc)\n        working-directory: rust\n        env:\n          HOTPATH_OUTPUT_FORMAT: json\n          HOTPATH_OUTPUT_PATH: /tmp/metrics/head_timing.json\n        run: |\n          cargo run --release -p mlt --features='__hotpath,__hotpath-alloc' -- \\\n            convert /tmp/benchmark-tiles/ /tmp/mlt-head-out/\n\n      - name: Checkout base\n        run: git checkout ${{ github.event.pull_request.base.sha }}\n\n      - name: Base benchmark (timing + alloc)\n        working-directory: rust\n        env:\n          HOTPATH_OUTPUT_FORMAT: json\n          HOTPATH_OUTPUT_PATH: /tmp/metrics/base_timing.json\n        run: |\n          cargo run --release -p mlt --features='__hotpath,__hotpath-alloc' -- \\\n            convert /tmp/benchmark-tiles/ /tmp/mlt-base-out/\n\n      - name: Save PR metadata\n        env:\n          PR_NUMBER: ${{ github.event.pull_request.number }}\n          BASE_REF: ${{ github.base_ref }}\n          HEAD_REF: ${{ github.head_ref }}\n        run: |\n          echo \"$PR_NUMBER\" > /tmp/metrics/pr_number.txt\n          echo \"$BASE_REF\" > /tmp/metrics/base_ref.txt\n          echo \"$HEAD_REF\" > /tmp/metrics/head_ref.txt\n\n      - uses: actions/upload-artifact@v7\n        with:\n          name: profile-metrics\n          path: /tmp/metrics/\n          retention-days: 1\n"
  },
  {
    "path": ".github/workflows/java-release.yml",
    "content": "name: Java - Release\n\non:\n  push:\n    tags:\n      - 'java-v*.*.*'\n  workflow_dispatch:\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v6.0.2\n\n      - name: Set up JDK\n        uses: actions/setup-java@v5\n        with:\n          java-version: '21'\n          distribution: 'temurin'\n\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - name: Get version from tag\n        id: get_version\n        run: |\n          version=\"$(just ci-extract-version java ${{ github.ref_name }})\"\n          echo version=\"$version\" >> \"$GITHUB_ENV\"\n\n      - name: Make gradlew executable\n        run: chmod +x ./java/gradlew\n\n      - name: Publish to Maven Central\n        working-directory: java\n        run: ./gradlew publishToMavenCentral -Pversion=${{ env.version }}\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n          ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }}\n          ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}\n\n      - name: Build CLI tool\n        working-directory: java\n        run: ./gradlew cli\n      - run: just -v assert-git-is-clean\n\n      - name: Rename CLI artifacts\n        run: |\n          cp java/mlt-cli/build/libs/encode.jar mlt-encode.jar\n          cp java/mlt-cli/build/libs/decode.jar mlt-decode.jar\n\n      - name: Add CLI artifacts to GitHub release\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          tag_name: java-v${{ env.version }}\n          fail_on_unmatched_files: true\n          name: java-v${{ env.version }}\n          files: |\n            mlt-encode.jar\n            mlt-decode.jar\n"
  },
  {
    "path": ".github/workflows/java.yml",
    "content": "name: Java CI\n\non:\n  workflow_call:\n  workflow_dispatch:\n\ndefaults:\n  run:\n    shell: bash\n\njobs:\n  test-java:\n    permissions:\n      id-token: write  # for OIDC Codecov\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n      - uses: ./.github/actions/mlt-setup-java\n      - run: just -v java::env-info\n      - if: matrix.os == 'ubuntu-latest'\n        name: Lint Java code. If fails, run `just java::fmt` to reformat, and `just java::lint` to see issues locally\n        run: just -v java::lint\n\n      - run: just -v java::test-coverage\n\n      - run: just -v java::test-cli\n\n      - name: Test Maven local publishing\n        run: just -v java::test-publish\n\n      - run: npm ci\n        working-directory: java/encoding-server\n      - run: npm run lint\n        working-directory: java/encoding-server\n      - run: just -v assert-git-is-clean\n\n      - name: Upload coverage to Codecov\n        if: github.repository_owner == 'maplibre'\n        uses: codecov/codecov-action@v6\n        with:\n          use_oidc: true\n          directory: java\n\n      - name: Test synthetic MLT generation\n        run: just -v java::ci-check-synthetic-mlts\n"
  },
  {
    "path": ".github/workflows/python-release.yml",
    "content": "# This file is autogenerated by maturin v1.12.4\n# To update, run the following command, but make sure to keep the adjustments that we did.\n#    maturin generate-ci github\nname: Python Release\n\non:\n  push:\n    branches:\n      - main\n    tags:\n      - 'python-mlt-v*'\n  pull_request:\n    paths:\n      - 'justfile'\n      - 'rust/mod.just'\n      - '.github/**'\n      - 'rust/**'\n      - 'test/**'\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  linux:\n    runs-on: ${{ matrix.platform.runner }}\n    strategy:\n      fail-fast: false\n      matrix:\n        platform:\n          - runner: ubuntu-22.04\n            target: x86_64\n          # FIXME: FSST does not seem to support 32bit targets\n          #- runner: ubuntu-22.04\n          #  target: x86\n          # FIXME: Python::initialize not avaliable for linking\n          #- runner: ubuntu-22.04\n          #  target: aarch64\n          # FIXME: error: unknown type name '__m128i' and related\n          #- runner: ubuntu-22.04\n          #  target: armv7\n          # FIXME: cpp standard mismatch\n          #- runner: ubuntu-22.04\n          #  target: s390x\n          # FIXME: error: unknown type name '__m128i' and related\n          #- runner: ubuntu-22.04\n          #  target: ppc64le\n    steps:\n      - uses: actions/checkout@v6\n      - uses: ./.github/actions/mlt-setup-rust\n      - uses: actions/setup-python@v6\n        with: { python-version: '3.x' }\n      - name: Build wheels\n        uses: PyO3/maturin-action@v1\n        with:\n          target: ${{ matrix.platform.target }}\n          args: --release --zig --out dist --find-interpreter --locked --compatibility pypi --future-incompat-report --manifest-path rust/mlt-py/Cargo.toml\n          sccache: false\n          manylinux: auto\n      - name: Upload wheels\n        uses: actions/upload-artifact@v7\n        with:\n          name: wheels-linux-${{ matrix.platform.target }}\n          path: dist\n\n  # FIXME: our --bin bins/stub-gen requires pyo3::Python::initialize, but unsure how to do that on musllinux. We don't need it for the library!\n  #musllinux:\n  #  runs-on: ${{ matrix.platform.runner }}\n  #  strategy:\n  #    fail-fast: false\n  #    matrix:\n  #      platform:\n  #        - runner: ubuntu-22.04\n  #          target: x86_64\n  #        - runner: ubuntu-22.04\n  #          target: x86\n  #        - runner: ubuntu-22.04\n  #          target: aarch64\n  #        - runner: ubuntu-22.04\n  #        # FIXME: FSST does not seem to support 32bit targets\n  #        #- runner: ubuntu-22.04\n  #        #  target: x86\n  #        # FIXME: Python::initialize not avaliable for linking\n  #        #- runner: ubuntu-22.04\n  #        # target: aarch64\n  #        # FIXME: error: unknown type name '__m128i' and related\n  #        #- runner: ubuntu-22.04\n  #        #  target: armv7\n  #  steps:\n  #    - uses: actions/checkout@v6\n  #    - uses: ./.github/actions/mlt-setup-rust\n  #    - uses: actions/setup-python@v6\n  #      with: { python-version: '3.x' }\n  #    - name: Build wheels\n  #      uses: PyO3/maturin-action@v1\n  #      with:\n  #        target: ${{ matrix.platform.target }}\n  #        args: --release --zig --out dist --find-interpreter --locked --compatibility pypi --future-incompat-report --manifest-path rust/mlt-py/Cargo.toml\n  #        sccache: false\n  #        manylinux: musllinux_1_2\n  #    - name: Upload wheels\n  #      uses: actions/upload-artifact@v7\n  #      with:\n  #        name: wheels-musllinux-${{ matrix.platform.target }}\n  #        path: dist\n\n  # FIXME: Failed to run `zig cc -v` with status exit code: 1: error: unable to create compilation: LibCStdLibHeaderNotFound\n  #windows:\n  #  runs-on: ${{ matrix.platform.runner }}\n  #  strategy:\n  #    fail-fast: false\n  #    matrix:\n  #      platform:\n  #        - runner: windows-latest\n  #          target: x64\n  #          python_arch: x64\n  #        - runner: windows-latest\n  #          target: x86\n  #          python_arch: x86\n  #        - runner: windows-11-arm\n  #          target: aarch64\n  #          python_arch: arm64\n  #  steps:\n  #    - uses: actions/checkout@v6\n  #    - uses: ./.github/actions/mlt-setup-rust\n  #    - uses: actions/setup-python@v6\n  #      with:\n  #        python-version: 3.13\n  #        architecture: ${{ matrix.platform.python_arch }}\n  #    - name: Build wheels\n  #      uses: PyO3/maturin-action@v1\n  #      with:\n  #        target: ${{ matrix.platform.target }}\n  #        args: --release --zig --out dist --find-interpreter --locked --compatibility pypi --future-incompat-report --manifest-path rust/mlt-py/Cargo.toml\n  #        sccache: false\n  #    - name: Upload wheels\n  #      uses: actions/upload-artifact@v7\n  #      with:\n  #        name: wheels-windows-${{ matrix.platform.target }}\n  #        path: dist\n\n  macos:\n    runs-on: ${{ matrix.platform.runner }}\n    strategy:\n      fail-fast: false\n      matrix:\n        platform:\n          - runner: macos-15-intel\n            target: x86_64\n          - runner: macos-latest\n            target: aarch64\n    steps:\n      - uses: actions/checkout@v6\n      - uses: ./.github/actions/mlt-setup-rust\n      - uses: actions/setup-python@v6\n        with: { python-version: '3.x' }\n      - name: Build wheels\n        uses: PyO3/maturin-action@v1\n        with:\n          target: ${{ matrix.platform.target }}\n          args: --release --zig --out dist --find-interpreter --locked --compatibility pypi --future-incompat-report --manifest-path rust/mlt-py/Cargo.toml\n          sccache: false\n      - name: Upload wheels\n        uses: actions/upload-artifact@v7\n        with:\n          name: wheels-macos-${{ matrix.platform.target }}\n          path: dist\n\n  sdist:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Build sdist\n        uses: PyO3/maturin-action@v1\n        with:\n          command: sdist\n          args: --out dist --manifest-path rust/mlt-py/Cargo.toml\n      - name: Upload sdist\n        uses: actions/upload-artifact@v7\n        with: { name: 'wheels-sdist', path: 'dist' }\n\n  release:\n    name: Release\n    runs-on: ubuntu-latest\n    if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}\n    needs:\n      - linux\n      # FIXME: does seem to require dev-only dependencys\n      #- musllinux\n      # FIXME: Failed to run `zig cc -v` with status exit code: 1: error: unable to create compilation: LibCStdLibHeaderNotFound\n      #- windows\n      - macos\n      - sdist\n    permissions:\n      # Use to sign the release artifacts\n      id-token: write\n      # Used to upload release artifacts\n      contents: write\n      # Used to generate artifact attestation\n      attestations: write\n    steps:\n      - uses: actions/download-artifact@v8\n      - name: Generate artifact attestation\n        uses: actions/attest-build-provenance@v4\n        with: { subject-path: 'wheels-*/*' }\n      - name: Install uv\n        if: ${{ startsWith(github.ref, 'refs/tags/') }}\n        uses: astral-sh/setup-uv@v7\n      - name: Publish to PyPI\n        if: ${{ startsWith(github.ref, 'refs/tags/') }}\n        run: uv publish 'wheels-*/*'\n"
  },
  {
    "path": ".github/workflows/rust.yml",
    "content": "name: Rust CI\n\non:\n  workflow_call:\n    inputs:\n      run-release:\n        type: boolean\n        default: false\n  workflow_dispatch:\n\njobs:\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n      - run: just -v rust::ci-check\n      - run: just -v rust::ci-lint\n\n  coverage:\n    name: Coverage\n    permissions:\n      id-token: write  # for OIDC Codecov\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n        with: { toolchain: nightly } # because of covs --doctests\n      - uses: taiki-e/install-action@v2\n        with: { tool: \"cargo-llvm-cov\" }\n      - run: just -v rust::ci-coverage\n      - name: Upload coverage to Codecov\n        if: github.repository_owner == 'maplibre'\n        uses: codecov/codecov-action@v6\n        with:\n          use_oidc: true\n          fail_ci_if_error: true\n          disable_search: true\n          files: rust/target/llvm-cov/lcov.info\n      - run: just -v assert-git-is-clean\n\n  test-wasm:\n    name: Test wasm\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n      - run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version-file: \"rust/mlt-wasm/.nvmrc\"\n          cache: \"npm\"\n          cache-dependency-path: \"rust/mlt-wasm/package-lock.json\"\n      - run: just -v rust::ci-test-wasm\n\n  fuzz:\n    name: Fuzz ${{ matrix.target }}\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - target: layer\n          - target: decoded_layer\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n        with: { toolchain: nightly }\n      - run: just -v rust::ci-fuzz-build ${{ matrix.target }}\n      - if: matrix.target == 'layer'\n        run: just -v rust::fuzz-seed ${{ matrix.target }}\n      - run: just -v rust::ci-fuzz-run ${{ matrix.target }}\n      - name: Generate fuzz coverage\n        if: github.repository_owner == 'maplibre'\n        run: just -v rust::ci-fuzz-cov ${{ matrix.target }}\n      - name: Upload fuzz coverage artifact\n        if: github.repository_owner == 'maplibre'\n        uses: actions/upload-artifact@v7\n        with:\n          name: fuzz-coverage-${{ matrix.target }}\n          path: rust/target/llvm-cov/fuzz-${{ matrix.target }}.lcov\n\n  #  MSRV Testing is not really needed at this stage of code completeness\n  #  test-msrv:\n  #    name: Test MSRV\n  #    runs-on: ubuntu-latest\n  #    steps:\n  #      - uses: actions/checkout@v6.0.2\n  #      - uses: ./.github/actions/mlt-setup-rust\n  #        with: { toolchain: msrv }\n  #      - run: just -v rust::ci-test-msrv\n\n  test-publish:\n    name: Test workspace publishing using --dry-run\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n      - name: Test workspace publishing\n        run: just -v rust::test-publish\n      - run: just -v assert-git-is-clean\n\n  build-release-binaries:\n    name: Build release binaries for ${{ matrix.target }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: ubuntu-latest\n            target: x86_64-unknown-linux-gnu\n            artifact_name: mlt-x86_64-unknown-linux-gnu.tar.gz\n          - os: ubuntu-24.04-arm\n            target: aarch64-unknown-linux-gnu\n            artifact_name: mlt-aarch64-unknown-linux-gnu.tar.gz\n          - os: macos-latest\n            target: aarch64-apple-darwin\n            artifact_name: mlt-aarch64-apple-darwin.tar.gz\n          - os: windows-latest\n            target: x86_64-pc-windows-msvc\n            artifact_name: mlt-x86_64-pc-windows-msvc.zip\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n      - run: just -v rust::ci-build-release ${{ matrix.target }} ${{ matrix.artifact_name }}\n      - name: Upload artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: ${{ matrix.artifact_name }}\n          path: ${{ matrix.artifact_name }}\n\n  build-wasm-npm:\n    name: Build @maplibre/mlt-wasm npm package\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: ./.github/actions/mlt-setup-rust\n        with: { toolchain: nightly }\n      - run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version-file: \"rust/mlt-wasm/.nvmrc\"\n          cache: \"npm\"\n          cache-dependency-path: \"rust/mlt-wasm/package-lock.json\"\n\n      - name: Build\n        run: just -v rust::wasm-build\n\n      - name: Merge license files into LICENSE.txt\n        working-directory: rust/mlt-wasm\n        run: |\n          {\n            echo \"===== MIT License =====\"\n            cat ../../LICENSE-MIT\n            echo\n            echo \"===== Apache License 2.0 =====\"\n            cat ../../LICENSE-APACHE\n          } > LICENSE.txt\n\n      - name: Pack\n        working-directory: rust/mlt-wasm\n        run: npm pack\n\n      - name: Upload npm package artifact\n        uses: actions/upload-artifact@v7\n        with:\n          name: mlt-wasm-npm\n          path: rust/mlt-wasm/maplibre-mlt-wasm-*.tgz\n\n  # This job checks if any of the previous jobs failed or were canceled.\n  # This approach also allows some jobs to be skipped if they are not needed.\n  ci-passed-rust:\n    needs:\n      - lint\n      - coverage\n      # re-enable once we have stability guarantees regarding MSRV\n      # - test-msrv\n      - test-wasm\n      - build-release-binaries\n      - test-publish\n      - build-wasm-npm\n    if: always()\n    runs-on: ubuntu-latest\n    steps:\n      - name: Result of the needed steps\n        run: echo \"${{ toJSON(needs) }}\"\n      - if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}\n        run: exit 1\n\n  # Release unpublished packages or create a PR with changes\n  release-plz:\n    needs: [ci-passed-rust]\n    if: |\n      always()\n      && needs.ci-passed-rust.result == 'success'\n      && inputs.run-release\n      && github.repository_owner == 'maplibre'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      id-token: write\n    concurrency:\n      group: release-plz-${{ github.ref }}\n      cancel-in-progress: false\n    steps:\n      - run: rustup update stable && rustup default stable\n      - uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v6.0.2\n        with: { fetch-depth: 0, persist-credentials: false }\n      - uses: ./.github/actions/mlt-setup-rust\n      - name: Publish to crates.io if crate's version is newer\n        uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128\n        id: release\n        with: { command: release, manifest_path: ./rust/Cargo.toml }\n        env:\n          GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}\n      - if: ${{ steps.release.outputs.releases_created == 'false' }}\n        name: If version is the same, create a PR proposing new version and changelog for the next release\n        uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128\n        with: { command: release-pr, manifest_path: ./rust/Cargo.toml }\n        env:\n          GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}\n    outputs:\n      releases_created: ${{ steps.release.outputs.releases_created }}\n      releases: ${{ steps.release.outputs.releases }}\n\n  upload-release-binaries:\n    name: Upload release binaries to GitHub releases\n    needs: [release-plz]\n    if: ${{ needs.release-plz.outputs.releases_created == 'true' }}\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: \"just\" }\n      - name: Get mlt version for release tag\n        id: get_version\n        shell: bash\n        run: just -v rust::ci-get-release-info mlt >> $GITHUB_OUTPUT\n      - name: Download all artifacts\n        uses: actions/download-artifact@v8\n        with: { path: artifacts }\n      - name: Add binaries to GitHub release\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          tag_name: ${{ steps.get_version.outputs.tag }}\n          files: |\n            artifacts/**/*.tar.gz\n            artifacts/**/*.zip\n          fail_on_unmatched_files: true\n\n  # Publish @maplibre/mlt-wasm to npm after mlt-wasm is released to crates.io.\n  # The npm package tarball was already built and uploaded as an artifact by build-wasm-npm.\n  publish-wasm-npm:\n    name: Publish @maplibre/mlt-wasm to npm\n    needs: [release-plz]\n    if: |\n      needs.release-plz.outputs.releases_created == 'true'\n      && contains(fromJSON(needs.release-plz.outputs.releases || '[]').*.package_name, 'mlt-wasm')\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      id-token: write\n    steps:\n      - name: Get mlt-wasm release version\n        id: version\n        env:\n          RELEASES: ${{ needs.release-plz.outputs.releases }}\n        run: |\n          echo \"value=$(echo \"$RELEASES\" | jq -r '.[] | select(.package_name == \"mlt-wasm\") | .version')\" >> $GITHUB_OUTPUT\n\n      - name: Set prerelease flag\n        run: |\n          version=\"${{ steps.version.outputs.value }}\"\n          echo version=\"$version\" >> \"$GITHUB_ENV\"\n          if [[ \"$version\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n            echo prerelease=false >> \"$GITHUB_ENV\"\n          else\n            echo prerelease=true >> \"$GITHUB_ENV\"\n          fi\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: \"24\"\n          registry-url: \"https://registry.npmjs.org\"\n\n      - name: Download npm package artifact\n        uses: actions/download-artifact@v8\n        with:\n          name: mlt-wasm-npm\n          path: mlt-wasm-pkg\n\n      - name: Release (GitHub) - Create Draft\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          name: mlt-wasm-js-v${{ env.version }}\n          tag_name: mlt-wasm-js-v${{ env.version }}\n          prerelease: ${{ env.prerelease }}\n          draft: true\n\n      - name: Publish npm package\n        run: |\n          package_tgz=\"$(find mlt-wasm-pkg -type f -name 'maplibre-mlt-wasm-*.tgz' -print -quit)\"\n          test -n \"$package_tgz\" || { echo \"::error::Package file not found in mlt-wasm-pkg\"; exit 1; }\n          npm publish \"$package_tgz\" --access=public\n\n      - name: Publish GitHub release (remove draft)\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          tag_name: mlt-wasm-js-v${{ env.version }}\n          name: mlt-wasm-js-v${{ env.version }}\n          draft: false\n"
  },
  {
    "path": ".github/workflows/ts-bump-version.yml",
    "content": "name: Manual TypeScript Version Bump\n\non:\n  workflow_dispatch:\n    inputs:\n      release_type:\n        type: choice\n        description: 'Type of version bump'\n        options:\n          - major\n          - minor\n          - patch\n          - premajor\n          - preminor\n          - prepatch\n          - prerelease\n\njobs:\n  js-bump-version:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - name: Update version, commit, and create tag\n        working-directory: ts\n        run: |\n          npm version ${{ github.event.inputs.release_type }} --no-git-tag-version\n          echo version=\"$(node -p \"require('./package.json').version\")\" >> \"$GITHUB_ENV\"\n\n      - name: Create Pull Request\n        uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1\n        with:\n          commit-message: bump @maplibre/mlt version to ${{ env.version }}\n          branch: mlt-js-version-${{ env.version }}\n          title: bump @maplibre/mlt version to ${{ env.version }}\n\n      - run: just -v assert-git-is-clean\n"
  },
  {
    "path": ".github/workflows/ts-release.yml",
    "content": "name: TypeScript - Release\n\npermissions:\n  id-token: write  # Required for trusted publishing\n  contents: write\n\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\njobs:\n  release-check:\n    name: Check if version changed\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        shell: bash\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - name: Check if version has been updated\n        id: check\n        uses: EndBug/version-check@095362f3cd50f690c8fa0e6afeea81834bd8d320\n        with:\n          file-name: ts/package.json\n\n      - run: just -v assert-git-is-clean\n\n    outputs:\n      publish: ${{ steps.check.outputs.changed }}\n\n  release:\n    name: Release\n    needs: release-check\n    if: ${{ needs.release-check.outputs.publish == 'true' }}\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: ts\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n\n      - uses: ./.github/actions/mlt-setup-node\n\n      - name: Get version\n        run: |\n          version=\"$(node -p \"require('./package.json').version\")\"\n          echo version=\"$version\" >> \"$GITHUB_ENV\"\n          if [[ \"$version\" =~ ^[0-9]+\\.[0-9]+\\.[0-9]+$ ]]; then\n            echo prerelease=false >> \"$GITHUB_ENV\"\n          else\n            echo prerelease=true >> \"$GITHUB_ENV\"\n          fi\n\n      - name: Install\n        run: npm ci\n\n      - name: Build\n        run: npm run build\n\n      - name: Merge license files into LICENSE.txt\n        run: |\n          {\n            echo \"===== MIT License =====\"\n            cat ../LICENSE-MIT\n            echo\n            echo \"===== Apache License 2.0 =====\"\n            cat ../LICENSE-APACHE\n          } > LICENSE.txt\n\n      - name: Release (GitHub) - Create Draft\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          name: js-v${{ env.version }}\n          tag_name: js-v${{ env.version }}\n          prerelease: ${{ env.prerelease }}\n          draft: true\n\n      - if: github.repository_owner == 'maplibre'\n        name: Publish npm package\n        run: npm publish --access=public\n\n      - name: Publish GitHub release (remove draft)\n        uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0\n        with:\n          tag_name: js-v${{ env.version }}\n          name: js-v${{ env.version }}\n          draft: false\n\n      - run: just -v assert-git-is-clean\n"
  },
  {
    "path": ".github/workflows/ts.yml",
    "content": "name: TypeScript CI\n\non:\n  workflow_call:\n  workflow_dispatch:\n\ndefaults:\n  run:\n    shell: bash\n\njobs:\n  test-js:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6.0.2\n      - uses: taiki-e/install-action@v2\n        with: { tool: 'just,cargo-binstall' }\n      - uses: ./.github/actions/mlt-setup-node\n      - name: Lint JS code. If fails, run `just ts::fmt` to reformat, and `just ts::lint` to see issues locally\n        run: just -v ts::lint\n      - run: just -v ts::test\n      - run: just -v ts::build\n      - name: Codecov upload\n        if: \"github.repository_owner == 'maplibre' && !cancelled()\"\n        uses: codecov/codecov-action@v6\n        with:\n          files: ts/coverage/coverage-final.json\n      - run: just -v assert-git-is-clean\n"
  },
  {
    "path": ".gitignore",
    "content": "*.class\n*.diff.geojson\n*.gcov\n*.log\n*.new.geojson\n*.actual.json\n.DS_Store\n.cache/\n.idea/\n.kotlin\n.venv\n.vscode/\nMODULE.bazel.lock\nbazel-*\nbin/\ncodecov*\ncoverage/\ndist/\nsite/\ntmp/\nnode_modules\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"cpp/vendor/googletest\"]\n\tpath = cpp/vendor/googletest\n\turl = https://github.com/google/googletest.git\n[submodule \"cpp/vendor/simde\"]\n\tpath = cpp/vendor/simde\n\turl = https://github.com/simd-everywhere/simde.git\n[submodule \"cpp/vendor/fastpfor\"]\n\tpath = cpp/vendor/fastpfor\n\turl = https://github.com/fast-pack/FastPFor.git\n[submodule \"cpp/vendor/json\"]\n\tpath = cpp/vendor/json\n\turl = https://github.com/nlohmann/json.git\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\n\n# exclusions should be separated with a pipe (|) character and a newline\nexclude: |\n  (?x)^(\n      test/expected/.*\n      |java/gradlew(\\.bat)?\n      |\\.cursor/rules/.*\n  )$\n\nci:\n  autoupdate_schedule: monthly\n  # sometimes fails https://github.com/keith/pre-commit-buildifier/issues/13\n  skip: [ buildifier ]\n\nrepos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: check-added-large-files\n        args: ['--maxkb=553']\n      - id: check-executables-have-shebangs\n      - id: check-json\n        exclude: '.+/tsconfig.json'\n      - id: check-shebang-scripts-are-executable\n        exclude: '.+\\.rs' # would be triggered by #![some_attribute]\n      - id: check-symlinks\n      - id: check-toml\n      - id: check-yaml\n        args: [ --allow-multiple-documents ]\n        exclude: mkdocs.yml\n      - id: destroyed-symlinks\n      - id: end-of-file-fixer\n      - id: mixed-line-ending\n        args: [ --fix=lf ]\n      - id: trailing-whitespace\n\n  - repo: https://github.com/pre-commit/mirrors-clang-format\n    rev: v22.1.2\n    hooks:\n      - id: clang-format\n        types: [ c++ ]\n        args: [\"-style=file:.clang-format\"]\n\n  - repo: https://github.com/Mateusz-Grzelinski/actionlint-py\n    rev: v1.7.12.24\n    hooks:\n      - id: actionlint\n        additional_dependencies: [ shellcheck-py ]\n        exclude: '\\.github/workflows/rust(-legacy)?\\.yml'\n\n  - repo: local\n    hooks:\n      - id: cargo-fmt\n        name: Rust Format\n        description: \"Automatically format Rust code with cargo fmt\"\n        entry: sh -c \"cd rust && cargo fmt --all\"\n        language: rust\n        pass_filenames: false\n\n  - repo: https://github.com/keith/pre-commit-buildifier\n    rev: 8.5.1.1\n    hooks:\n      - id: buildifier\n\n  - repo: https://github.com/biomejs/pre-commit\n    rev: v2.4.10\n    hooks:\n      - id: biome-check\n        args: [--unsafe]\n        exclude: '.*\\.json$'\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Code of conduct\n\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/maplibre/maplibre/blob/main/CODE_OF_CONDUCT.md)\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright 2024 MapLibre contributors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-CC0",
    "content": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n    HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n  i. the right to reproduce, adapt, distribute, perform, display,\n     communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n     likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n     subject to the limitations in paragraph 4(a), below;\n  v. rights protecting the extraction, dissemination, use and reuse of data\n     in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n     European Parliament and of the Council of 11 March 1996 on the legal\n     protection of databases, and under any national implementation\n     thereof, including any amended or successor version of such\n     directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n     world based on applicable law or treaty, and any national\n     implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n    warranties of any kind concerning the Work, express, implied,\n    statutory or otherwise, including without limitation warranties of\n    title, merchantability, fitness for a particular purpose, non\n    infringement, or the absence of latent or other defects, accuracy, or\n    the present or absence of errors, whether or not discoverable, all to\n    the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n    that may apply to the Work or any use thereof, including without\n    limitation any person's Copyright and Related Rights in the Work.\n    Further, Affirmer disclaims responsibility for obtaining any necessary\n    consents, permissions or other rights required for any use of the\n    Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n    party to this document and has no duty or obligation with respect to\n    this CC0 or use of the Work.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "MIT License\n\nCopyright (c) 2024 MapLibre contributors\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "MODULE.bazel",
    "content": "module(name = \"maplibre-tile-spec\")\n\nbazel_dep(name = \"platforms\", version = \"1.0.0\")\nbazel_dep(name = \"rules_cc\", version = \"0.2.8\")\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-dark-bg.svg\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg\">\n    <img alt=\"MapLibre Logo\" src=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg\" width=\"200\">\n  </picture>\n</p>\n\n# MapLibre Tile (MLT)\n\n> [!NOTE]\n> [The specification](https://maplibre.org/maplibre-tile-spec/specification/) is deemed stable as of October 2025. However, as a living standard, experimental features may continue to evolve. Implementations and integrations are being developed, refer to the [implementation status](https://maplibre.org/maplibre-tile-spec/implementation-status/) page for the current status.\n\nThe MapLibre Tile specification is mainly inspired by the [Mapbox Vector Tile (MVT)](https://github.com/mapbox/vector-tile-spec) specification,\nbut has been redesigned from the ground up to address the challenges of rapidly growing geospatial data volumes\nand complex next-generation geospatial source formats as well as to leverage the capabilities of modern hardware and APIs.\nMLT is specifically designed for modern and next generation graphics APIs to enable high-performance processing and rendering of\nlarge (planet-scale) 2D and 2.5 basemaps. In particular, MLT offers the following features:\n- **Improved compression ratio**: up to 6x on large encoded tiles, based on a column oriented layout with recursively applied (custom)\n    lightweight encodings. This leads to reduced latency, storage, and egress costs and, in particular, improved cache utilization\n- **Better decoding performance**: fast lightweight encodings which can be used in combination with SIMD/vectorization instructions\n- **Support for linear referencing and m-values** to efficiently support the upcoming next generation source formats such as Overture Maps (GeoParquet)\n- **Support 3D coordinates**, i.e. elevation\n- **Support complex types**, including nested properties, lists and maps\n- **Improved processing performance**, based on storage and in-memory formats that are specifically designed for modern GL APIs,\nallowing for efficient processing on both CPU and GPU. The formats are designed to be loaded into\nGPU buffers with little or no additional processing\n\n📝 For a more in-depth exploration of MLT have a look at the [following slides](https://github.com/mactrem/presentations/blob/main/FOSS4G_2024_Europe/FOSS4G_2024_Europe.pdf), watch\n[this talk](https://www.youtube.com/watch?v=YHcoAFcsES0) or read [this paper](https://dl.acm.org/doi/10.1145/3748636.3763208) by MLT inventor Markus Tremmel.\n\n## Directory Structure\n\n- `/spec` MLT specification and related documentation\n- `/test` Test MVT tiles and the expected MLT conversion results\n- `/java` Java encoder for converting MVT to MLT, as well as a decoder parsing MLT to an in-memory representation.\n- `/js` Javascript decoder\n- `/rust` Rust decoder\n\n## Getting Involved\n\nJoin the `#maplibre-tile-format` Slack channel at OSM US: get an invite at https://slack.openstreetmap.us/\n\n### Development\n\n* This project is easier to develop with [just](https://just.systems/man/en/), a modern alternative to `make`.\n* To get a list of available commands, run `just`.\n* To run tests, use `just test`.\n\n## License\n\n* All project documentation and specification content is licensed under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) - Public Domain Dedication.\n* All code is dual licensed under [Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0) and [MIT license](http://opensource.org/licenses/MIT), at your option.\n* Tile test data in the `/test` directory has different licenses depending on the source of that data.\n\n### Contribution\n\nUnless you explicitly state otherwise, any code contribution intentionally\nsubmitted for inclusion in the work by you, as defined in the\nApache-2.0 license, shall be dual licensed as above, without any\nadditional terms or conditions. Similarly, any documentation or specification\ncontributions shall be licensed under CC0 1.0 Universal.\n\n## Citing\n\nIf you use MapLibre Tile in your research, please cite our paper:\n\n```bibtex\n@inproceedings{tremmel2025maplibretile,\n  title     = {MapLibre Tile: A Next Generation Vector Tile Format},\n  author    = {Tremmel, Markus and Zink, Roland},\n  booktitle = {Proceedings of the 33rd ACM International Conference on Advances in Geographic Information Systems},\n  series    = {SIGSPATIAL '25},\n  year      = {2025},\n  pages     = {1118--1121},\n  doi       = {10.1145/3748636.3763208},\n  url       = {https://doi.org/10.1145/3748636.3763208}\n}\n```\n"
  },
  {
    "path": "SECURITY_POLICY.txt",
    "content": "For an up-to-date policy refer to\nhttps://github.com/maplibre/maplibre/blob/main/SECURITY_POLICY.txt\n"
  },
  {
    "path": "biome.json",
    "content": "{\n  \"$schema\": \"https://biomejs.dev/schemas/2.0.5/schema.json\",\n  \"css\": {\n    \"formatter\": {\n      \"indentWidth\": 2,\n      \"indentStyle\": \"space\"\n    }\n  },\n  \"javascript\": {\n    \"formatter\": {\n      \"indentWidth\": 2,\n      \"indentStyle\": \"space\"\n    }\n  }\n}\n"
  },
  {
    "path": "cpp/.clang-tidy",
    "content": "---\nChecks: [\n    android-*,\n    boost-*,\n    bugprone-*,\n    clang-analyzer-core*,\n    clang-analyzer-cplusplus*,\n    clang-analyzer-deadcode*,\n    clang-analyzer-optin.cplusplus*,\n    clang-analyzer-optin.performance.Padding,\n    clang-analyzer-security*,\n    clang-diagnostic-*,\n    cppcoreguidelines-avoid-goto,\n    cppcoreguidelines-no-malloc,\n    google-*,\n    llvm-*,\n    misc-*,\n    modernize-*,\n    performance-*,\n    portability-*,\n    readability-*,\n    -bugprone-branch-clone,\n    -bugprone-easily-swappable-parameters,\n    -bugprone-exception-escape,\n    -bugprone-forward-declaration-namespace,\n    -bugprone-forwarding-reference-overload,\n    -bugprone-implicit-widening-of-multiplication-result,\n    -bugprone-macro-parentheses,\n    -bugprone-narrowing-conversions,\n    -bugprone-reserved-identifier,\n    -bugprone-signed-char-misuse,\n    -bugprone-sizeof-expression,\n    -bugprone-unchecked-optional-access,\n    -bugprone-unhandled-exception-at-new,\n    -bugprone-unhandled-self-assignment,\n    -clang-analyzer-optin.cplusplus.UninitializedObject,\n    -clang-analyzer-core.NonNullParamChecker,\n    -clang-analyzer-core.NullDereference,\n    -clang-analyzer-core.uninitialized.Assign,\n    -clang-analyzer-core.UndefinedBinaryOperatorResult,\n    -clang-analyzer-security.FloatLoopCounter,\n    -google-build-explicit-make-pair,\n    -google-build-namespaces,\n    -google-build-using-namespace,\n    -google-default-arguments,\n    -google-explicit-constructor,\n    -google-objc-global-variable-declaration,\n    -google-readability-braces-around-statements,\n    -google-readability-casting,\n    -google-readability-function-size,\n    -google-readability-function,\n    -google-readability-namespace-comments,\n    -google-readability-todo,\n    -google-runtime-int,\n    -google-runtime-references,\n    -llvm-else-after-return,\n    -llvm-header-guard,\n    -llvm-include-order,\n    -llvm-namespace-comment,\n    -llvm-qualified-auto,\n    -llvm-twine-local,\n    -misc-confusable-identifiers,\n    -misc-const-correctness,\n    -misc-no-recursion,\n    -misc-non-private-member-variables-in-classes,\n    -misc-static-assert,\n    -modernize-avoid-bind,\n    -modernize-avoid-c-arrays,\n    -modernize-concat-nested-namespaces,\n    -modernize-macro-to-enum,\n    -modernize-pass-by-value,\n    -modernize-return-braced-init-list,\n    -modernize-use-auto,\n    -modernize-use-default-member-init,\n    -modernize-use-emplace,\n    -modernize-use-equals-default,\n    -modernize-use-nodiscard,\n    -modernize-use-nullptr,\n    -modernize-use-trailing-return-type,\n    -performance-move-const-arg,\n    -performance-noexcept-move-constructor,\n    -performance-no-int-to-ptr,\n    -performance-unnecessary-value-param,\n    -readability-avoid-const-params-in-decls,\n    -readability-braces-around-statements,\n    -readability-const-return-type,\n    -readability-container-size-empty,\n    -readability-convert-member-functions-to-static,\n    -readability-else-after-return,\n    -readability-function-cognitive-complexity,\n    -readability-function-size,\n    -readability-identifier-length,\n    -readability-identifier-naming,\n    -readability-implicit-bool-conversion,\n    -readability-inconsistent-declaration-parameter-name,\n    -readability-isolate-declaration,\n    -readability-magic-numbers,\n    -readability-make-member-function-const,\n    -readability-named-parameter,\n    -readability-non-const-parameter,\n    -readability-qualified-auto,\n    -readability-redundant-access-specifiers,\n    -readability-redundant-declaration,\n    -readability-redundant-member-init,\n    -readability-redundant-string-init,\n    -readability-simplify-boolean-expr,\n    -readability-static-accessed-through-instance\n    -readability-static-definition-in-anonymous-namespace,\n    -readability-suspicious-call-argument,\n    -readability-uppercase-literal-suffix,\n    -readability-use-anyofallof,\n    -modernize-loop-convert, # since C++20 this complains about reverse loops with iterators, but good ranges support only landed in clang 15\n    -performance-enum-size,\n    -misc-include-cleaner,\n    -readability-redundant-inline-specifier,\n    -readability-avoid-nested-conditional-operator,\n    -cppcoreguidelines-pro-type-static-cast-downcast, # no RTTI\n]\nWarningsAsErrors: '*'\nHeaderFilterRegex: '.*'\nCheckOptions:\n  - key:   performance-unnecessary-value-param.AllowedTypes\n    value: 'exception_ptr'\n"
  },
  {
    "path": "cpp/.gitignore",
    "content": "/build\n/.cache\ncompile_commands.json\ncmake_test_discovery*\n"
  },
  {
    "path": "cpp/.nvmrc",
    "content": "24.13\n"
  },
  {
    "path": "cpp/BUILD.bazel",
    "content": "load(\"@rules_cc//cc:cc_library.bzl\", \"cc_library\")\n\ncc_library(\n    name = \"mlt_internal_headers\",\n    hdrs = glob([\"src/mlt/**/*.hpp\"]),\n    include_prefix = \"\",\n    strip_include_prefix = \"src\",\n    visibility = [\"//visibility:private\"],\n)\n\ncc_library(\n    name = \"mlt_cpp\",\n    srcs = glob([\n        \"src/mlt/**/*.cpp\",\n    ]),\n    hdrs = glob([\n        \"include/mlt/**/*.hpp\",\n    ]),\n    copts = select({\n        \"@platforms//os:windows\": [\n            \"/std:c++20\",\n        ],\n        \"//conditions:default\": [\n            \"-std=c++20\",\n            \"-Wall\",\n            \"-Wextra\",\n        ],\n    }),\n    defines = [\n        \"MLT_WITH_JSON=0\",\n        \"MLT_WITH_FASTPFOR=0\",\n    ],\n    implementation_deps = [\":mlt_internal_headers\"],\n    include_prefix = \"\",\n    strip_include_prefix = \"include\",\n    visibility = [\"//visibility:public\"],\n)\n"
  },
  {
    "path": "cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25)\nproject(mlt-cpp LANGUAGES CXX)\n\nlist(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)\ninclude(AddCXXCompilerFlag)\n\n# generate compile_commands.json https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\nset(CMAKE_CXX_STANDARD 20)\n\nif(PROJECT_IS_TOP_LEVEL AND CMAKE_CXX_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_CXX_FLAGS_COVERAGE \"-g -O0 --coverage\")\n    if(CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n        string(APPEND CMAKE_CXX_FLAGS_COVERAGE \" -fprofile-abs-path\")\n    endif()\n    set(CMAKE_EXE_LINKER_FLAGS_COVERAGE \"--coverage\")\n    set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE \"--coverage\")\n    set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE \"--coverage\")\nendif()\n\nadd_library(mlt-cpp STATIC)\n\noption(MLT_WITH_JSON \"Include JSON support\" ON)\noption(MLT_WITH_FASTPFOR \"Include FastPFor support\" ON)\noption(MLT_WITH_FASTPFOR_SIMD \"Include SIMD/NEON support in FastPFor decoding\" ON)\noption(MLT_WITH_TESTS \"Include Tests\" ON)\noption(MLT_WITH_TOOLS \"Include CLI tools\" ON)\n\nset_target_properties(\n    mlt-cpp\n    PROPERTIES\n    INTERFACE_MAPLIBRE_NAME \"MapLibre Tile Format\"\n    INTERFACE_MAPLIBRE_URL \"https://github.com/maplibre/maplibre-tile-spec\"\n    INTERFACE_MAPLIBRE_AUTHOR \"MapLibre\"\n    INTERFACE_MAPLIBRE_LICENSE \"${PROJECT_SOURCE_DIR}/../LICENSE-APACHE\"\n)\n\nadd_cxx_compiler_flag(-Wall)\nadd_cxx_compiler_flag(-Werror)\nadd_cxx_compiler_flag(-Wextra)\n\nadd_cxx_compiler_flag(-Wdeprecated-declarations)\nadd_cxx_compiler_flag(-Winvalid-offsetof)\nadd_cxx_compiler_flag(-Wno-block-capture-autoreleasing)\nadd_cxx_compiler_flag(-Wno-bool-conversion)\nadd_cxx_compiler_flag(-Wno-c++11-extensions)\nadd_cxx_compiler_flag(-Wno-comma)\nadd_cxx_compiler_flag(-Wno-constant-conversion)\nadd_cxx_compiler_flag(-Wno-conversion)\nadd_cxx_compiler_flag(-Wno-empty-body)\nadd_cxx_compiler_flag(-Wno-enum-conversion)\nadd_cxx_compiler_flag(-Wno-exit-time-destructors)\nadd_cxx_compiler_flag(-Wno-float-conversion)\nadd_cxx_compiler_flag(-Wno-four-char-constants)\nadd_cxx_compiler_flag(-Wno-implicit-fallthrough)\nadd_cxx_compiler_flag(-Wno-infinite-recursion)\nadd_cxx_compiler_flag(-Wno-missing-braces)\nadd_cxx_compiler_flag(-Wno-missing-field-initializers)\nadd_cxx_compiler_flag(-Wno-move)\nadd_cxx_compiler_flag(-Wno-newline-eof)\nadd_cxx_compiler_flag(-Wno-non-literal-null-conversion)\nadd_cxx_compiler_flag(-Wno-non-virtual-dtor)\nadd_cxx_compiler_flag(-Wno-objc-literal-conversion)\nadd_cxx_compiler_flag(-Wno-overloaded-virtual)\nadd_cxx_compiler_flag(-Wno-range-loop-analysis)\nadd_cxx_compiler_flag(-Wno-return-type)\nadd_cxx_compiler_flag(-Wno-semicolon-before-method-body)\nadd_cxx_compiler_flag(-Wno-shadow)\nadd_cxx_compiler_flag(-Wno-sign-conversion)\nadd_cxx_compiler_flag(-Wno-trigraphs)\nadd_cxx_compiler_flag(-Wno-uninitialized)\nadd_cxx_compiler_flag(-Wno-unknown-pragmas)\nadd_cxx_compiler_flag(-Wno-unused-function)\nadd_cxx_compiler_flag(-Wno-unused-label)\nadd_cxx_compiler_flag(-Wno-unused-parameter)\nadd_cxx_compiler_flag(-Wno-unused-variable)\nadd_cxx_compiler_flag(-Wparentheses)\nadd_cxx_compiler_flag(-Wshorten-64-to-32)\nadd_cxx_compiler_flag(-Wswitch)\nadd_cxx_compiler_flag(-Wunused-value)\nadd_cxx_compiler_flag(-fstrict-aliasing)\nadd_cxx_compiler_flag(-wd4061) # MSVC: enum member not handled in switch\nadd_cxx_compiler_flag(-wd4514) # MSVC: unreferenced inline function has been removed\nadd_cxx_compiler_flag(-wd4710) # MSVC: function not inlined\nadd_cxx_compiler_flag(-wd4820) # MSVC: padding added after data member\n\ntarget_include_directories(mlt-cpp\n    PUBLIC ${PROJECT_SOURCE_DIR}/include\n    PRIVATE ${PROJECT_SOURCE_DIR}/src\n)\n\nlist(APPEND MLT_INCLUDE_FILES\n    ${PROJECT_SOURCE_DIR}/include/mlt/common.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/decoder.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/feature.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/geometry.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/geometry_vector.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/layer.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/polyfill.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/projection.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/properties.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/tile.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/metadata/stream.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/metadata/tileset.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/util/buffer_stream.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/util/packed_bitset.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/util/noncopyable.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/util/stl.hpp\n    ${PROJECT_SOURCE_DIR}/include/mlt/util/varint.hpp\n)\n\nlist(APPEND MLT_SRC_FILES\n    ${PROJECT_SOURCE_DIR}/src/mlt/decoder.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/decode/geometry.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/decode/int.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/decode/int.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/decode/property.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/decode/string.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/feature.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/geometry_vector.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/layer.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/metadata/stream.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/metadata/tileset.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/properties.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/morton_curve.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/raw.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/rle.cpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/rle.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/space_filling_curve.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/vectorized.hpp\n    ${PROJECT_SOURCE_DIR}/src/mlt/util/zigzag.hpp\n)\n\nif(MLT_WITH_JSON)\n    list(APPEND MLT_INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mlt/json.hpp)\nendif(MLT_WITH_JSON)\n\ntarget_sources(mlt-cpp PRIVATE\n    ${MLT_INCLUDE_FILES}\n    ${MLT_SRC_FILES}\n)\n\ninclude(\"${PROJECT_SOURCE_DIR}/vendor/fastpfor.cmake\")\n\n# json\nif(MLT_WITH_JSON)\n    message(STATUS \"[MLT] Including JSON support\")\n\n    add_subdirectory(\"${PROJECT_SOURCE_DIR}/vendor/json\" \"${CMAKE_CURRENT_BINARY_DIR}/json\" EXCLUDE_FROM_ALL SYSTEM)\n\n    set(json_SOURCE_DIR \"${PROJECT_SOURCE_DIR}/vendor/json\")\n    target_include_directories(mlt-cpp PUBLIC \"${json_SOURCE_DIR}/include\")\n    target_compile_definitions(mlt-cpp PUBLIC MLT_WITH_JSON=1)\nelse()\n    message(STATUS \"[MLT] No JSON support\")\nendif(MLT_WITH_JSON)\n\nif(MLT_WITH_TESTS)\n    include(CTest)\n    add_subdirectory(${PROJECT_SOURCE_DIR}/test EXCLUDE_FROM_ALL)\nendif(MLT_WITH_TESTS)\n\nif(MLT_WITH_TOOLS)\n    add_subdirectory(${PROJECT_SOURCE_DIR}/tool EXCLUDE_FROM_ALL)\nendif(MLT_WITH_TOOLS)\n\nexport(TARGETS ${MLT_EXPORT_TARGETS} FILE MLTTargets.cmake)\n"
  },
  {
    "path": "cpp/CTestCustom.cmake",
    "content": "# Exclude vendor/third-party code from coverage reports\nset(CTEST_CUSTOM_COVERAGE_EXCLUDE\n    ${CTEST_CUSTOM_COVERAGE_EXCLUDE}\n    \".*/vendor/.*\"\n    \".*/build/.*/_deps/.*\"\n    \".*/test/.*\"\n)\n"
  },
  {
    "path": "cpp/README.md",
    "content": "# maplibre-tile-spec/cpp\n\nA C++ implementation of the MapLibre Tile (MLT) vector tile format.\n\n## Status\n\nDecoder only, partial support for encodings.\n\nCMake and Bazel build support.\n\n## Build\n\n```bash\ngit submodule update --init --recursive\ncmake -GNinja -Bbuild -S.\ncmake --build build --target mlt-cpp-test mlt-cpp-json\n```\n\n## Test\n\n```\nbuild/test/mlt-cpp-test\n```\n\n## Use\n\nTo decode a tile:\n\n- Deserialize the tileset metadata\n- Create a `Decoder`\n- Call `decode` with the metadata and a view on the raw tile data\n\nTo use the standard packing of metadata and data within a tile, use `decodeTile`.\n\n```cpp\n#include <mlt/decoder.hpp>\n#include <mlt/metadata/tileset.hpp>\n\n...\n\nauto metadata = mlt::metadata::tileset::read({metadataBuffer.data(), metadataBuffer.size()});\n\nmlt::Decoder decoder;\nconst auto tile = decoder.decode({mltBuffer.data(), mltBuffer.size()}, metadata);\n\nconst auto tile2 = decoder.decodeTile({tileData.data(), tileData.size()});\n```\n\n## Tools\n\nA simple application which dumps a tile/metadata file pair to JSON format.\n\n```bash\nbuild/tool/mlt-cpp-json ../test/expected/tag0x01/bing/4-12-6.mlt\n```\n"
  },
  {
    "path": "cpp/bazel/check/BUILD.bazel",
    "content": "load(\"@rules_cc//cc:cc_binary.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"sse42\",\n    srcs = [\"sse42.c\"],\n    copts = select({\n        \"@platforms//os:windows\": [\n            \"/EHsc\",\n            \"/arch:SSE2\",\n        ],\n        \"//conditions:default\": [\n            \"-march=native\",\n            \"-msse4.2\",\n        ],\n    }),\n)\n\ncc_binary(\n    name = \"avx\",\n    srcs = [\"avx.c\"],\n    copts = select({\n        \"@platforms//os:windows\": [\n            \"/EHsc\",\n            \"/arch:AVX\",\n        ],\n        \"//conditions:default\": [\n            \"-march=native\",\n            \"-mavx\",\n        ],\n    }),\n)\n\ncc_binary(\n    name = \"avx2\",\n    srcs = [\"avx2.c\"],\n    copts = select({\n        \"@platforms//os:windows\": [\n            \"/EHsc\",\n            \"/arch:AVX2\",\n        ],\n        \"//conditions:default\": [\n            \"-march=native\",\n            \"-mavx2\",\n        ],\n    }),\n)\n"
  },
  {
    "path": "cpp/bazel/check/avx.c",
    "content": "#include<immintrin.h>\nint main(){\n__m128 x=_mm_set1_ps(0.5);\nx=_mm_permute_ps(x,1);\nreturn _mm_movemask_ps(x);\n}\n"
  },
  {
    "path": "cpp/bazel/check/avx2.c",
    "content": "#include<immintrin.h>\nint main(){\n__m256i x=_mm256_set1_epi32(5);\nx=_mm256_add_epi32(x,x);\nreturn _mm256_movemask_epi8(x);\n}\n"
  },
  {
    "path": "cpp/bazel/check/sse42.c",
    "content": "#include<smmintrin.h>\nint main(){\n__m128 x=_mm_set1_ps(0.5);\nx=_mm_dp_ps(x,x,0x77);\nreturn _mm_movemask_ps(x);\n}\")\n"
  },
  {
    "path": "cpp/cmake/AddCXXCompilerFlag.cmake",
    "content": "# - Adds a compiler flag if it is supported by the compiler\n#\n# This function checks that the supplied compiler flag is supported and then\n# adds it to the corresponding compiler flags\n#\n#  add_cxx_compiler_flag(<FLAG> [<VARIANT>])\n#\n# - Example\n#\n# include(AddCXXCompilerFlag)\n# add_cxx_compiler_flag(-Wall)\n# add_cxx_compiler_flag(-no-strict-aliasing RELEASE)\n# Requires CMake 2.6+\n\nif(__add_cxx_compiler_flag)\n  return()\nendif()\nset(__add_cxx_compiler_flag INCLUDED)\n\ninclude(CheckCXXCompilerFlag)\n\nfunction(mangle_compiler_flag FLAG OUTPUT)\n  string(TOUPPER \"HAVE_CXX_FLAG_${FLAG}\" SANITIZED_FLAG)\n  string(REPLACE \"+\" \"X\" SANITIZED_FLAG ${SANITIZED_FLAG})\n  string(REGEX REPLACE \"[^A-Za-z_0-9]\" \"_\" SANITIZED_FLAG ${SANITIZED_FLAG})\n  string(REGEX REPLACE \"_+\" \"_\" SANITIZED_FLAG ${SANITIZED_FLAG})\n  set(${OUTPUT} \"${SANITIZED_FLAG}\" PARENT_SCOPE)\nendfunction(mangle_compiler_flag)\n\nfunction(add_cxx_compiler_flag FLAG)\n  mangle_compiler_flag(\"${FLAG}\" MANGLED_FLAG)\n  set(OLD_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n  set(CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS} ${FLAG}\")\n  check_cxx_compiler_flag(\"${FLAG}\" ${MANGLED_FLAG})\n  set(CMAKE_REQUIRED_FLAGS \"${OLD_CMAKE_REQUIRED_FLAGS}\")\n  if(${MANGLED_FLAG})\n    set(VARIANT ${ARGV1})\n    if(ARGV1)\n      string(TOUPPER \"_${VARIANT}\" VARIANT)\n    endif()\n    set(CMAKE_CXX_FLAGS${VARIANT} \"${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}\" PARENT_SCOPE)\n  endif()\nendfunction()\n\nfunction(add_required_cxx_compiler_flag FLAG)\n  mangle_compiler_flag(\"${FLAG}\" MANGLED_FLAG)\n  set(OLD_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n  set(CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS} ${FLAG}\")\n  check_cxx_compiler_flag(\"${FLAG}\" ${MANGLED_FLAG})\n  set(CMAKE_REQUIRED_FLAGS \"${OLD_CMAKE_REQUIRED_FLAGS}\")\n  if(${MANGLED_FLAG})\n    set(VARIANT ${ARGV1})\n    if(ARGV1)\n      string(TOUPPER \"_${VARIANT}\" VARIANT)\n    endif()\n    set(CMAKE_CXX_FLAGS${VARIANT} \"${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}\" PARENT_SCOPE)\n    set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} ${FLAG}\" PARENT_SCOPE)\n    set(CMAKE_SHARED_LINKER_FLAGS \"${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}\" PARENT_SCOPE)\n    set(CMAKE_MODULE_LINKER_FLAGS \"${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}\" PARENT_SCOPE)\n    set(CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS} ${FLAG}\" PARENT_SCOPE)\n  else()\n    message(FATAL_ERROR \"Required flag '${FLAG}' is not supported by the compiler\")\n  endif()\nendfunction()\n\nfunction(check_cxx_warning_flag FLAG)\n  mangle_compiler_flag(\"${FLAG}\" MANGLED_FLAG)\n  set(OLD_CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS}\")\n  # Add -Werror to ensure the compiler generates an error if the warning flag\n  # doesn't exist.\n  set(CMAKE_REQUIRED_FLAGS \"${CMAKE_REQUIRED_FLAGS} -Werror ${FLAG}\")\n  check_cxx_compiler_flag(\"${FLAG}\" ${MANGLED_FLAG})\n  set(CMAKE_REQUIRED_FLAGS \"${OLD_CMAKE_REQUIRED_FLAGS}\")\nendfunction()\n"
  },
  {
    "path": "cpp/include/mlt/common.hpp",
    "content": "#pragma once\n\n#include <mlt/polyfill.hpp>\n\n#include <string_view>\n#include <type_traits>\n\nnamespace mlt {\n\nusing DataView = std::string_view;\n\ntemplate <typename T, std::size_t N>\nconstexpr std::size_t countof(T (&)[N]) {\n    return N;\n}\n\n/// `std::underlying_type` that doesn't fail when given a simple type\ntemplate <typename T, bool = std::is_enum_v<T>>\nstruct underlying_type {\n    using type = T;\n};\ntemplate <typename T>\nstruct underlying_type<T, true> : ::std::underlying_type<T> {};\ntemplate <class T>\nusing underlying_type_t = typename underlying_type<T>::type;\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/coordinate.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n#include <vector>\n\nnamespace mlt {\n\nstruct Coordinate {\n    float x;\n    float y;\n\n    Coordinate(float x_, float y_) noexcept\n        : x(x_),\n          y(y_) {}\n\n    bool operator==(const Coordinate& other) const noexcept { return x == other.x && y == other.y; }\n    bool operator!=(const Coordinate& other) const noexcept { return !(*this == other); }\n};\nusing CoordVec = std::vector<Coordinate>;\n\nstruct TileCoordinate {\n    std::uint32_t x;\n    std::uint32_t y;\n    std::uint32_t z;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/decoder.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/tile.hpp>\n#include <mlt/util/noncopyable.hpp>\n#include <mlt/geometry.hpp>\n\n#include <memory>\n\nnamespace mlt {\n\nclass Decoder : public util::noncopyable {\npublic:\n    using Geometry = geometry::Geometry;\n    using GeometryFactory = geometry::GeometryFactory;\n\n    Decoder(bool supportFastPFOR);\n    Decoder(std::unique_ptr<GeometryFactory>&&, bool supportFastPFOR);\n    ~Decoder() noexcept;\n    Decoder(Decoder&&) = delete;\n    Decoder& operator=(Decoder&&) = delete;\n\n    /// Decode a tile\n    MapLibreTile decode(DataView);\n    MapLibreTile decode(BufferStream);\n\nprivate:\n    struct Impl;\n    std::unique_ptr<Impl> impl;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/feature.hpp",
    "content": "#pragma once\n\n#include <mlt/properties.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <memory>\n#include <optional>\n\nnamespace mlt {\n\nnamespace geometry {\nclass Geometry;\n}\nclass Layer;\nclass Feature : public util::noncopyable {\npublic:\n    using id_t = std::uint64_t;\n    using extent_t = std::uint32_t;\n    using Geometry = geometry::Geometry;\n\n    Feature() = delete;\n    Feature(Feature&&) noexcept = default;\n    Feature& operator=(Feature&&) = delete;\n\n    /// Construct a feature\n    /// @param id Feature identifier, or nullopt if the feature has no ID\n    /// @param geometry Feature geometry, required\n    /// @param index Index of the property in the layer\n    Feature(std::optional<id_t> id, std::unique_ptr<Geometry>&&, std::uint32_t index);\n\n    ~Feature() noexcept;\n\n    std::optional<id_t> getID() const noexcept { return ident; }\n    std::uint32_t getIndex() const noexcept { return index; }\n    const Geometry& getGeometry() const noexcept { return *geometry; }\n    std::optional<Property> getProperty(const std::string& key, const Layer&) const;\n\nprivate:\n    std::optional<id_t> ident;\n    std::uint32_t index; // index of the property in the layer\n    std::unique_ptr<Geometry> geometry;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/geometry.hpp",
    "content": "#pragma once\n\n#include <mlt/coordinate.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <memory>\n#include <span>\n#include <vector>\n\nnamespace mlt::geometry {\n\nclass Geometry : public util::noncopyable {\npublic:\n    using GeometryType = metadata::tileset::GeometryType;\n\nprotected:\n    Geometry(GeometryType type_) noexcept\n        : type(type_) {}\n\npublic:\n    virtual ~Geometry() noexcept = default;\n\n    const auto& getTriangles() const { return triangles; }\n    void setTriangles(std::span<const std::uint32_t> triangles_) noexcept { triangles = triangles_; }\n\n    const metadata::tileset::GeometryType type;\n\nprivate:\n    std::span<const std::uint32_t> triangles;\n};\n\nclass Point : public Geometry {\npublic:\n    Point(const Coordinate& coord) noexcept\n        : Geometry(GeometryType::POINT),\n          coordinate(coord) {}\n\n    const Coordinate& getCoordinate() const noexcept { return coordinate; }\n\nprivate:\n    const Coordinate coordinate;\n};\n\nclass MultiPoint : public Geometry {\npublic:\n    MultiPoint(CoordVec coords) noexcept\n        : Geometry(GeometryType::MULTIPOINT),\n          coordinates(std::move(coords)) {}\n\n    const CoordVec& getCoordinates() const noexcept { return coordinates; }\n\nprotected:\n    MultiPoint(CoordVec coords, GeometryType type_) noexcept\n        : Geometry(type_),\n          coordinates(std::move(coords)) {}\n\nprivate:\n    CoordVec coordinates;\n};\n\nclass LineString : public MultiPoint {\npublic:\n    LineString(CoordVec coords) noexcept\n        : MultiPoint(std::move(coords), GeometryType::LINESTRING) {}\n\nprivate:\n};\n\nclass LinearRing : public MultiPoint {\npublic:\n    LinearRing(CoordVec coords) noexcept\n        : MultiPoint(std::move(coords)) {}\n\nprivate:\n};\n\nclass MultiLineString : public Geometry {\npublic:\n    MultiLineString(std::vector<CoordVec> lineStrings_) noexcept\n        : Geometry(GeometryType::MULTILINESTRING),\n          lineStrings(std::move(lineStrings_)) {}\n\n    const std::vector<CoordVec>& getLineStrings() const noexcept { return lineStrings; }\n\nprivate:\n    std::vector<CoordVec> lineStrings;\n};\n\nclass Polygon : public Geometry {\npublic:\n    using Ring = CoordVec;\n    using RingVec = std::vector<Ring>;\n\n    Polygon(RingVec rings_) noexcept\n        : Geometry(GeometryType::POLYGON),\n          rings(std::move(rings_)) {}\n\n    const RingVec& getRings() const noexcept { return rings; }\n\nprivate:\n    RingVec rings;\n};\n\nclass MultiPolygon : public Geometry {\npublic:\n    using Ring = CoordVec;\n    using RingVec = std::vector<CoordVec>;\n\n    MultiPolygon(std::vector<RingVec> polygons_) noexcept\n        : Geometry(GeometryType::MULTIPOLYGON),\n          polygons(std::move(polygons_)) {}\n\n    const std::vector<RingVec>& getPolygons() const noexcept { return polygons; }\n\nprivate:\n    std::vector<RingVec> polygons;\n};\n\nclass GeometryFactory {\npublic:\n    GeometryFactory() = default;\n    virtual ~GeometryFactory() = default;\n\n    virtual std::unique_ptr<Geometry> createPoint(const Coordinate& coord) const {\n        return std::make_unique<Point>(coord);\n    }\n    virtual std::unique_ptr<Geometry> createMultiPoint(CoordVec&& coords) const {\n        return std::make_unique<MultiPoint>(std::move(coords));\n    }\n    virtual std::unique_ptr<Geometry> createLineString(CoordVec&& coords) const {\n        return std::make_unique<LineString>(std::move(coords));\n    }\n    virtual std::unique_ptr<Geometry> createLinearRing(CoordVec&& coords) const {\n        return std::make_unique<LineString>(std::move(coords));\n    }\n    virtual std::unique_ptr<Geometry> createPolygon(std::vector<CoordVec>&& rings) const {\n        return std::make_unique<Polygon>(std::move(rings));\n    }\n    virtual std::unique_ptr<Geometry> createMultiLineString(std::vector<CoordVec>&& lineStrings) const {\n        return std::make_unique<MultiLineString>(std::move(lineStrings));\n    }\n    virtual std::unique_ptr<Geometry> createMultiPolygon(std::vector<MultiPolygon::RingVec>&& polys) const {\n        return std::make_unique<MultiPolygon>(std::move(polys));\n    }\n};\n\n} // namespace mlt::geometry\n"
  },
  {
    "path": "cpp/include/mlt/geometry_vector.hpp",
    "content": "#pragma once\n\n#include <mlt/geometry.hpp>\n#include <mlt/util/morton_curve.hpp>\n\n#include <algorithm>\n#include <cassert>\n\nnamespace mlt::geometry {\n\nstruct MortonSettings {\n    unsigned numBits;\n    unsigned coordinateShift;\n};\n\nenum class VertexBufferType : std::uint32_t {\n    MORTON,\n    VEC_2,\n    VEC_3\n};\n\nstruct TopologyVector : public util::noncopyable {\n    TopologyVector(std::vector<std::uint32_t>&& geometryOffsets_,\n                   std::vector<std::uint32_t>&& partOffsets_,\n                   std::vector<std::uint32_t>&& ringOffsets_) noexcept\n        : geometryOffsets(geometryOffsets_),\n          partOffsets(partOffsets_),\n          ringOffsets(ringOffsets_) {}\n\n    const std::vector<std::uint32_t>& getGeometryOffsets() const noexcept { return geometryOffsets; }\n\n    const std::vector<std::uint32_t>& getPartOffsets() const noexcept { return partOffsets; }\n\n    const std::vector<std::uint32_t>& getRingOffsets() const noexcept { return ringOffsets; }\n\nprivate:\n    std::vector<std::uint32_t> geometryOffsets;\n    std::vector<std::uint32_t> partOffsets;\n    std::vector<std::uint32_t> ringOffsets;\n};\n\nstruct GeometryVector : public util::noncopyable {\n    using GeometryType = metadata::tileset::GeometryType;\n\n    virtual ~GeometryVector() = default;\n\n    std::uint32_t getNumGeometries() const noexcept { return numGeometries; }\n    bool isSingleGeometryType() const noexcept { return singleType; }\n    virtual GeometryType getGeometryType(std::size_t index) const noexcept = 0;\n    virtual bool containsPolygonGeometry() const noexcept = 0;\n\n    std::vector<std::unique_ptr<Geometry>> getGeometries(const GeometryFactory&) const;\n\nprotected:\n    GeometryVector(std::uint32_t numGeometries_,\n                   bool singleType_,\n\n                   std::vector<std::uint32_t>&& indexBuffer_,\n                   std::vector<std::int32_t>&& vertexBuffer_,\n                   VertexBufferType vertexBufferType_,\n                   std::vector<std::uint32_t>&& vertexOffsets_,\n                   std::vector<std::uint32_t>&& triangleCounts_,\n                   std::optional<TopologyVector>&& topologyVector_,\n                   std::optional<MortonSettings> mortonSettings_ = {}) noexcept\n        : numGeometries(numGeometries_),\n          scale(1.0f),\n          singleType(singleType_),\n          indexBuffer(std::move(indexBuffer_)),\n          vertexBuffer(std::move(vertexBuffer_)),\n          vertexBufferType(vertexBufferType_),\n          vertexOffsets(std::move(vertexOffsets_)),\n          triangleCounts(std::move(triangleCounts_)),\n          topologyVector(std::move(topologyVector_)),\n          mortonSettings(mortonSettings_) {}\n\n    void applyTriangles(Geometry&,\n                        std::uint32_t& triangleOffset,\n                        std::uint32_t& indexBufferOffset,\n                        std::uint32_t totalVertices,\n                        bool multiPolygon) const;\n\nprotected:\n    std::uint32_t numGeometries;\n    float scale;\n    bool singleType;\n    std::vector<std::uint32_t> indexBuffer;\n    std::vector<std::int32_t> vertexBuffer;\n    VertexBufferType vertexBufferType;\n    std::vector<std::uint32_t> vertexOffsets;\n    std::vector<std::uint32_t> triangleCounts;\n    std::optional<TopologyVector> topologyVector;\n    std::optional<MortonSettings> mortonSettings;\n};\n\nstruct GpuVector : public GeometryVector {\n    GpuVector(std::uint32_t numGeometries_,\n              bool singleType_,\n\n              std::vector<std::uint32_t>&& triangleCounts_,\n              std::vector<std::uint32_t>&& indexBuffer_,\n              std::vector<std::int32_t>&& vertexBuffer_,\n              VertexBufferType vertexBufferType_,\n              std::optional<TopologyVector>&& topologyVector_) noexcept\n        : GeometryVector(numGeometries_,\n                         singleType_,\n                         std::move(indexBuffer_),\n                         std::move(vertexBuffer_),\n                         vertexBufferType_,\n                         {},\n                         std::move(triangleCounts_),\n                         std::move(topologyVector_)) {}\n\nprivate:\n};\n\nstruct ConstGpuVector : public GpuVector {\n    ConstGpuVector(std::uint32_t numGeometries_,\n\n                   GeometryType geometryType_,\n                   std::vector<std::uint32_t>&& triangleCounts_,\n                   std::vector<std::uint32_t>&& indexBuffer_,\n                   std::vector<std::int32_t>&& vertexBuffer_,\n                   VertexBufferType vertexBufferType_,\n                   std::optional<TopologyVector>&& topologyVector_) noexcept\n        : GpuVector(numGeometries_,\n                    /*singleType=*/true,\n                    std::move(triangleCounts_),\n                    std::move(indexBuffer_),\n                    std::move(vertexBuffer_),\n                    vertexBufferType_,\n                    std::move(topologyVector_)),\n          geometryType(geometryType_) {}\n\n    GeometryType getGeometryType(std::size_t) const noexcept override { return geometryType; }\n\n    bool containsPolygonGeometry() const noexcept override {\n        return geometryType == GeometryType::POLYGON || geometryType == GeometryType::MULTIPOLYGON;\n    }\n\nprivate:\n    GeometryType geometryType;\n};\n\nstruct FlatGpuVector : public GpuVector {\n    FlatGpuVector(std::vector<GeometryType>&& geometryTypes_,\n                  std::vector<std::uint32_t>&& triangleCounts_,\n                  std::vector<std::uint32_t>&& indexBuffer_,\n                  std::vector<std::int32_t>&& vertexBuffer_,\n                  std::optional<TopologyVector>&& topologyVector_ = {}) noexcept\n        : GpuVector(static_cast<std::uint32_t>(geometryTypes_.size()),\n                    /*singleType=*/false,\n                    std::move(triangleCounts_),\n                    std::move(indexBuffer_),\n                    std::move(vertexBuffer_),\n                    VertexBufferType::VEC_2,\n                    std::move(topologyVector_)),\n          geometryTypes(std::move(geometryTypes_)) {}\n\n    GeometryType getGeometryType(std::size_t index) const noexcept override {\n        assert(index < geometryTypes.size());\n        return geometryTypes[index];\n    }\n\n    bool containsPolygonGeometry() const noexcept override {\n        // TODO: cache?  types can't change\n        return std::ranges::any_of(geometryTypes, [](const auto type) {\n            return type == GeometryType::POLYGON || type == GeometryType::MULTIPOLYGON;\n        });\n    }\n\nprivate:\n    std::vector<GeometryType> geometryTypes;\n};\n\nstruct CpuVector : public GeometryVector {\n    CpuVector(std::uint32_t numGeometries_,\n              bool singleType_,\n              TopologyVector&& topologyVector_,\n              std::vector<std::uint32_t>&& vertexOffsets_,\n              std::vector<std::int32_t>&& vertexBuffer_,\n              VertexBufferType vertexBufferType_,\n              std::optional<MortonSettings> mortonSettings_) noexcept\n        : GeometryVector(numGeometries_,\n                         singleType_,\n                         /*indexBuffer=*/{},\n                         std::move(vertexBuffer_),\n                         vertexBufferType_,\n                         std::move(vertexOffsets_),\n                         {},\n                         std::move(topologyVector_),\n                         mortonSettings_) {}\n\n    Coordinate getVertex(std::size_t index) const {\n        if (!vertexOffsets.empty() && !mortonSettings) {\n            const auto vertexOffset = vertexOffsets[index] * 2;\n            return {static_cast<float>(vertexBuffer[vertexOffset]), static_cast<float>(vertexBuffer[vertexOffset + 1])};\n        }\n        if (!vertexOffsets.empty() && mortonSettings) {\n            // TODO: move decoding of the morton codes on the GPU in the vertex shader\n            const auto vertexOffset = vertexOffsets[index];\n            const auto mortonEncodedVertex = vertexBuffer[vertexOffset];\n            // TODO: improve performance -> inline calculation and move to decoding of VertexBuffer\n            return util::MortonCurve::decode(\n                mortonEncodedVertex, mortonSettings->numBits, mortonSettings->coordinateShift);\n        }\n\n        return {static_cast<float>(vertexBuffer[2 * index]), static_cast<float>(vertexBuffer[(2 * index) + 1])};\n    }\n};\n\nstruct ConstGeometryVector : public CpuVector {\n    ConstGeometryVector(std::uint32_t numGeometries_,\n                        GeometryType geometryType_,\n                        VertexBufferType vertexBufferType_,\n                        TopologyVector&& topologyVector_,\n                        std::vector<std::uint32_t>&& vertexOffsets_,\n                        std::vector<std::int32_t>&& vertexBuffer_,\n                        std::optional<MortonSettings> mortonSettings_) noexcept\n        : CpuVector(numGeometries_,\n                    /*singleType=*/true,\n                    std::move(topologyVector_),\n                    std::move(vertexOffsets_),\n                    std::move(vertexBuffer_),\n                    vertexBufferType_,\n                    mortonSettings_),\n          geometryType(geometryType_) {}\n\n    GeometryType getGeometryType(std::size_t) const noexcept override { return geometryType; }\n\n    bool containsPolygonGeometry() const noexcept override {\n        return geometryType == GeometryType::POLYGON || geometryType == GeometryType::MULTIPOLYGON;\n    }\n\nprivate:\n    GeometryType geometryType;\n};\n\nstruct FlatGeometryVector : public CpuVector {\n    FlatGeometryVector(std::vector<GeometryType>&& geometryTypes_,\n                       TopologyVector&& topologyVector_,\n                       std::vector<std::uint32_t>&& vertexOffsets_,\n                       std::vector<std::int32_t>&& vertexBuffer_,\n                       VertexBufferType vertexBufferType_,\n                       std::optional<MortonSettings> mortonSettings_) noexcept\n        : CpuVector(static_cast<std::uint32_t>(geometryTypes_.size()),\n                    /*singleType=*/false,\n                    std::move(topologyVector_),\n                    std::move(vertexOffsets_),\n                    std::move(vertexBuffer_),\n                    vertexBufferType_,\n                    mortonSettings_),\n          geometryTypes(geometryTypes_) {}\n\n    GeometryType getGeometryType(std::size_t index) const noexcept override {\n        assert(index < geometryTypes.size());\n        return geometryTypes[index];\n    }\n\n    bool containsPolygonGeometry() const noexcept override {\n        // TODO: cache?  types can't change\n        return std::ranges::any_of(geometryTypes, [](const auto type) {\n            return type == GeometryType::POLYGON || type == GeometryType::MULTIPOLYGON;\n        });\n    }\n\nprivate:\n    std::vector<GeometryType> geometryTypes;\n};\n\n} // namespace mlt::geometry\n"
  },
  {
    "path": "cpp/include/mlt/json.hpp",
    "content": "#pragma once\n\n#include <sstream>\n#if MLT_WITH_JSON\n#include <mlt/common.hpp>\n#include <mlt/coordinate.hpp>\n#include <mlt/feature.hpp>\n#include <mlt/geometry.hpp>\n#include <mlt/layer.hpp>\n#include <mlt/projection.hpp>\n#include <mlt/tile.hpp>\n\n#include <nlohmann/json.hpp>\n\n#include <cmath>\n#include <iterator>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <utility>\n\nnamespace mlt {\nnamespace json {\n\nusing json = nlohmann::json;\n\nnamespace detail {\n\n#pragma region JSON utils\n/// Create a json array object with pre-allocated space\ninline json buildArray(std::size_t reservedSize) {\n    json array = json::array();\n    assert(array.is_array());\n    array.get_ref<nlohmann::json::array_t&>().reserve(reservedSize);\n    return array;\n}\n\n/// Add to a json array the result of a function applied to each element in a range\ntemplate <typename TRange, typename TFunc>\n    requires requires(TFunc f, typename std::ranges::range_rvalue_reference_t<TRange> v) {\n        { f(v) } -> std::same_as<json>;\n    }\ninline json append(TRange sourceRange, json&& array, TFunc transform) {\n    assert(array.is_array());\n    std::ranges::transform(\n        std::forward<TRange>(sourceRange), std::back_inserter(array), std::forward<TFunc>(transform));\n    return std::move(array);\n}\n\n/// Convert a collection range into a json array by applying the given function to each element\ntemplate <typename TRange, typename TFunc>\n    requires requires(TFunc f, typename std::ranges::range_rvalue_reference_t<TRange> v) {\n        { f(v) } -> std::same_as<json>;\n    }\ninline json buildArray(TRange sourceRange, TFunc transform) {\n    return append(sourceRange, buildArray(std::ranges::size(sourceRange)), std::forward<TFunc>(transform));\n}\n#pragma endregion JSON utils\n\n#pragma region Geometry\n/// Build the coordinate representation for a single coordinate that has already been projected\ninline json buildProjectedCoordinateArray(const Coordinate& coord) {\n    return json::array({coord.x, coord.y});\n}\n\n/// Build the coordinate representation for a single coordinate\ninline json buildCoordinateArray(const Coordinate& coord, const Projection& projection) {\n    return buildProjectedCoordinateArray(projection.project(coord));\n}\n\n/// Build the coordinate representation for a collection of coordinates\ninline json buildCoordinatesArray(const CoordVec& coords, const Projection& projection) {\n    return buildArray(coords, [&](const auto& coord) { return buildCoordinateArray(coord, projection); });\n}\n\n/// Build the coordinate representation for a polygon, consisting of the rings concatenated to the shell\ninline json buildPolygonCoords(const std::vector<CoordVec>& polyRings, const Projection& projection) {\n    auto result = buildArray(polyRings.size());\n    return append(polyRings, std::move(result), [&](const auto& lineString) {\n        return buildCoordinatesArray(lineString, projection);\n    });\n}\n\n/// Create the value type for the \"geometry\" entry in a GeoJSON feature with the given coordinate representation\ninline json buildGeometryElement(std::string_view type, json&& coords) {\n    return {\n        {\"type\", type},\n        {\"coordinates\", std::move(coords)},\n    };\n}\n\ninline json buildGeometryElement(const geometry::Point& point, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return {\n            {\"type\", \"Point\"},\n            {\"coordinates\", buildCoordinateArray(point.getCoordinate(), projection)},\n        };\n    } else {\n        std::ostringstream ss;\n        ss << \"POINT (\" << point.getCoordinate().x << \" \" << point.getCoordinate().y << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::LineString& line, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"LineString\", buildCoordinatesArray(line.getCoordinates(), projection));\n    } else {\n        std::ostringstream ss;\n        ss << \"LINESTRING (\";\n        const auto& coords = line.getCoordinates();\n        for (std::size_t i = 0, n = coords.size(); i < n; ++i) {\n            ss << (i == 0 ? \"\" : \", \") << coords[i].x << \" \" << coords[i].y;\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::LinearRing& line, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"LineString\", buildCoordinatesArray(line.getCoordinates(), projection));\n    } else {\n        std::ostringstream ss;\n        ss << \"LINESTRING (\";\n        const auto& coords = line.getCoordinates();\n        for (std::size_t i = 0; i < coords.size(); ++i) {\n            ss << (i == 0 ? \"\" : \", \") << coords[i].x << \" \" << coords[i].y;\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::MultiPoint& points, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"MultiPoint\", buildCoordinatesArray(points.getCoordinates(), projection));\n    } else {\n        std::ostringstream ss;\n        ss << \"MULTIPOINT (\";\n        const auto& coords = points.getCoordinates();\n        for (std::size_t i = 0; i < coords.size(); ++i) {\n            ss << (i == 0 ? \"\" : \", \") << \"(\" << coords[i].x << \" \" << coords[i].y << \")\";\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::MultiLineString& mls, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"MultiLineString\", buildArray(mls.getLineStrings(), [&](const auto& lineString) {\n                                        return buildCoordinatesArray(lineString, projection);\n                                    }));\n    } else {\n        std::ostringstream ss;\n        ss << \"MULTILINESTRING (\";\n        const auto& lineStrings = mls.getLineStrings();\n        for (std::size_t i = 0, n = lineStrings.size(); i < n; ++i) {\n            if (i != 0) {\n                ss << \", \";\n            }\n            ss << \"(\";\n            const auto& coords = lineStrings[i];\n            for (std::size_t j = 0; j < coords.size(); ++j) {\n                ss << (j == 0 ? \"\" : \", \") << coords[j].x << \" \" << coords[j].y;\n            }\n            ss << \")\";\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::Polygon& poly, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"Polygon\", buildPolygonCoords(poly.getRings(), projection));\n    } else {\n        std::ostringstream ss;\n        ss << \"POLYGON (\";\n        const auto& rings = poly.getRings();\n        for (std::size_t i = 0, n = rings.size(); i < n; ++i) {\n            if (i != 0) {\n                ss << \", \";\n            }\n            ss << \"(\";\n            const auto& coords = rings[i];\n            for (std::size_t j = 0; j <= coords.size(); ++j) {\n                // Wrap around to the first coordinate to close the ring.\n                // See `getLineStringCoords`\n                const auto& coord = coords[j % coords.size()];\n                ss << (j == 0 ? \"\" : \", \") << coord.x << \" \" << coord.y;\n            }\n            ss << \")\";\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildGeometryElement(const geometry::MultiPolygon& poly, const Projection& projection, bool geoJSON) {\n    if (geoJSON) {\n        return buildGeometryElement(\"MultiPolygon\", buildArray(poly.getPolygons(), [&](const auto& poly) {\n                                        return buildPolygonCoords(poly, projection);\n                                    }));\n    } else {\n        std::ostringstream ss;\n        ss << \"MULTIPOLYGON (\";\n        const auto& polygons = poly.getPolygons();\n        for (std::size_t i = 0; i < polygons.size(); ++i) {\n            if (i != 0) {\n                ss << \", \";\n            }\n            ss << \"(\";\n            const auto& rings = polygons[i];\n            for (std::size_t j = 0; j < rings.size(); ++j) {\n                if (j != 0) {\n                    ss << \", \";\n                }\n                ss << \"(\";\n                const auto& coords = rings[j];\n                for (std::size_t k = 0; k <= coords.size(); ++k) {\n                    const auto& coord = coords[k % coords.size()];\n                    ss << (k == 0 ? \"\" : \", \") << coord.x << \" \" << coord.y;\n                }\n                ss << \")\";\n            }\n            ss << \")\";\n        }\n        ss << \")\";\n        return ss.str();\n    }\n}\n\ninline json buildAnyGeometryElement(const geometry::Geometry& geometry, const Projection& projection, bool geoJSON) {\n    switch (geometry.type) {\n        case metadata::tileset::GeometryType::POINT:\n            return buildGeometryElement(static_cast<const geometry::Point&>(geometry), projection, geoJSON);\n        case metadata::tileset::GeometryType::LINESTRING:\n            return buildGeometryElement(static_cast<const geometry::LineString&>(geometry), projection, geoJSON);\n        case metadata::tileset::GeometryType::POLYGON:\n            return buildGeometryElement(static_cast<const geometry::Polygon&>(geometry), projection, geoJSON);\n        case metadata::tileset::GeometryType::MULTIPOINT:\n            return buildGeometryElement(static_cast<const geometry::MultiPoint&>(geometry), projection, geoJSON);\n        case metadata::tileset::GeometryType::MULTILINESTRING:\n            return buildGeometryElement(static_cast<const geometry::MultiLineString&>(geometry), projection, geoJSON);\n        case metadata::tileset::GeometryType::MULTIPOLYGON:\n            return buildGeometryElement(static_cast<const geometry::MultiPolygon&>(geometry), projection, geoJSON);\n        default:\n            throw std::runtime_error(\"Unsupported geometry type \" + std::to_string(std::to_underlying(geometry.type)));\n    }\n}\n#pragma endregion Geometry\n\n#pragma region Properties\nstruct PropertyVisitor {\n    template <typename T>\n    std::optional<json> encodeFloatingPoint(T value, std::string_view prefix) const {\n        if (std::isfinite(value)) {\n            return json(value);\n        }\n        if (std::isnan(value)) {\n            return json(std::string(prefix) + \"::NAN\");\n        }\n        if (std::signbit(value)) {\n            return json(std::string(prefix) + \"::NEG_INFINITY\");\n        }\n        return json(std::string(prefix) + \"::INFINITY\");\n    }\n\n    std::optional<json> operator()(float value) const { return encodeFloatingPoint(value, \"f32\"); }\n    std::optional<json> operator()(double value) const { return encodeFloatingPoint(value, \"f64\"); }\n\n    template <typename T>\n    std::optional<json> operator()(const T& value) const {\n        return value;\n    }\n};\n\ninline json buildProperties(const Layer& layer, const Feature& feature) {\n    auto result = json::object();\n    for (const auto& [key, _] : layer.getProperties()) {\n        if (const auto property = feature.getProperty(key, layer); property) {\n            if (auto json = std::visit(PropertyVisitor(), *property); json) {\n                result[key] = std::move(*json);\n            }\n        }\n    }\n    return result;\n}\n#pragma endregion Properties\n\n} // namespace detail\n\ninline json toJSON(const Layer& layer, const Feature& feature, const Projection& projection, bool geoJSON) {\n    auto result = json{\n        {\"geometry\", detail::buildAnyGeometryElement(feature.getGeometry(), projection, geoJSON)},\n    };\n    if (const auto id = feature.getID(); id.has_value()) {\n        result[\"id\"] = *id;\n    }\n    if (geoJSON) {\n        result[\"type\"] = \"Feature\";\n    }\n    if (!layer.getProperties().empty()) {\n        result[\"properties\"] = detail::buildProperties(layer, feature);\n    }\n    return result;\n}\n\ninline json toJSON(const Layer& layer, const TileCoordinate& tileCoord, bool geoJSON) {\n    const auto projection = Projection{layer.getExtent(), tileCoord};\n    const auto features = std::ranges::views::all(layer.getFeatures());\n    return {{\"name\", layer.getName()},\n            {\"extent\", layer.getExtent()},\n            {\"features\", detail::buildArray(features, [&](const auto& feature) {\n                 return toJSON(layer, feature, projection, geoJSON);\n             })}};\n}\n\ninline json toJSON(const MapLibreTile& tile, const TileCoordinate& tileCoord, bool geoJSON) {\n    const auto layers = std::ranges::views::all(tile.getLayers());\n    return {\n        {\"layers\", detail::buildArray(layers, [&](const auto& layer) { return toJSON(layer, tileCoord, geoJSON); })},\n    };\n}\n\n} // namespace json\n} // namespace mlt\n\n#endif\n"
  },
  {
    "path": "cpp/include/mlt/layer.hpp",
    "content": "#pragma once\n\n#include <mlt/feature.hpp>\n#include <mlt/properties.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <vector>\n\nnamespace mlt {\n\nnamespace geometry {\nstruct GeometryVector;\n}\n\nclass Layer : public util::noncopyable {\npublic:\n    using extent_t = std::uint32_t;\n\n    Layer() = delete;\n    Layer(std::string name_,\n          extent_t extent_,\n          std::unique_ptr<geometry::GeometryVector>&& geometryVector_,\n          std::vector<Feature> features_,\n          PropertyVecMap properties_) noexcept;\n    ~Layer();\n\n    Layer(Layer&&) noexcept = default;\n    Layer& operator=(Layer&&) = default;\n\n    const std::string& getName() const noexcept { return name; }\n    extent_t getExtent() const noexcept { return extent; }\n    const std::vector<Feature>& getFeatures() const noexcept { return features; }\n    const PropertyVecMap& getProperties() const { return properties; }\n\nprivate:\n    std::string name;\n    extent_t extent;\n\n    // Retain the geometry vector because features may reference data from it rather than making copies\n    std::unique_ptr<geometry::GeometryVector> geometryVector;\n\n    std::vector<Feature> features;\n    PropertyVecMap properties;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/metadata/stream.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/noncopyable.hpp>\n#include <mlt/util/varint.hpp>\n\n#include <optional>\n#include <memory>\n\nnamespace mlt::metadata::stream {\n\nenum class DictionaryType : std::uint32_t {\n    NONE = 0,\n    SINGLE = 1,\n    SHARED = 2,\n    VERTEX = 3,\n    MORTON = 4,\n    FSST = 5,\n    VALUE_COUNT = 6,\n};\n\nenum class LengthType : std::uint32_t {\n    VAR_BINARY = 0,\n    GEOMETRIES = 1,\n    PARTS = 2,\n    RINGS = 3,\n    TRIANGLES = 4,\n    SYMBOL = 5,\n    DICTIONARY = 6,\n    VALUE_COUNT = 7,\n};\n\nenum class PhysicalLevelTechnique : std::uint32_t {\n    NONE = 0,\n    /// Preferred, tends to produce the best compression ratio and decoding performance.\n    /// But currently limited to 32-bit integer.\n    FAST_PFOR = 1,\n    /// Can produce better results in combination with a heavyweight compression scheme like Gzip.\n    /// Simple compression scheme where the decoder are easier to implement compared to FastPfor.\n    VARINT = 2,\n    VALUE_COUNT = 3,\n};\n\nenum class LogicalLevelTechnique : std::uint32_t {\n    NONE = 0,\n    DELTA = 1,\n    COMPONENTWISE_DELTA = 2,\n    RLE = 3,\n    MORTON = 4,\n    PSEUDODECIMAL = 5,\n    VALUE_COUNT = 6,\n};\n\nenum class OffsetType : std::uint32_t {\n    VERTEX = 0,\n    INDEX = 1,\n    STRING = 2,\n    KEY = 3,\n    VALUE_COUNT = 4,\n};\n\nenum class PhysicalStreamType : std::uint32_t {\n    PRESENT = 0,\n    DATA = 1,\n    OFFSET = 2,\n    LENGTH = 3,\n    VALUE_COUNT = 4,\n};\n\nclass LogicalStreamType : public util::noncopyable {\npublic:\n    LogicalStreamType(DictionaryType type) noexcept\n        : dictionaryType(type) {}\n    LogicalStreamType(OffsetType type) noexcept\n        : offsetType(type) {}\n    LogicalStreamType(LengthType type) noexcept\n        : lengthType(type) {}\n\n    LogicalStreamType() = delete;\n    LogicalStreamType(LogicalStreamType&&) noexcept = default;\n    LogicalStreamType& operator=(LogicalStreamType&&) noexcept = default;\n\n    const std::optional<DictionaryType>& getDictionaryType() const noexcept { return dictionaryType; }\n    const std::optional<OffsetType>& getOffsetType() const noexcept { return offsetType; }\n    const std::optional<LengthType>& getLengthType() const noexcept { return lengthType; }\n\nprivate:\n    std::optional<DictionaryType> dictionaryType;\n    std::optional<OffsetType> offsetType;\n    std::optional<LengthType> lengthType;\n};\n\nclass StreamMetadata : public util::noncopyable {\npublic:\n    StreamMetadata() = delete;\n    StreamMetadata(PhysicalStreamType physicalStreamType_,\n                   std::optional<LogicalStreamType> logicalStreamType_,\n                   LogicalLevelTechnique logicalLevelTechnique1_,\n                   LogicalLevelTechnique logicalLevelTechnique2_,\n                   PhysicalLevelTechnique physicalLevelTechnique_,\n                   std::uint32_t numValues_,\n                   std::uint32_t byteLength_) noexcept\n        : physicalStreamType(physicalStreamType_),\n          logicalStreamType(std::move(logicalStreamType_)),\n          logicalLevelTechnique1(logicalLevelTechnique1_),\n          logicalLevelTechnique2(logicalLevelTechnique2_),\n          physicalLevelTechnique(physicalLevelTechnique_),\n          numValues(numValues_),\n          byteLength(byteLength_) {}\n    virtual ~StreamMetadata() noexcept = default;\n    StreamMetadata(StreamMetadata&&) noexcept = default;\n    StreamMetadata& operator=(StreamMetadata&&) noexcept = default;\n\n    virtual LogicalLevelTechnique getMetadataType() const noexcept { return LogicalLevelTechnique::NONE; }\n\n    static std::unique_ptr<StreamMetadata> decode(BufferStream&);\n\n    PhysicalStreamType getPhysicalStreamType() const { return physicalStreamType; }\n    const std::optional<LogicalStreamType>& getLogicalStreamType() const { return logicalStreamType; }\n    LogicalLevelTechnique getLogicalLevelTechnique1() const { return logicalLevelTechnique1; }\n    LogicalLevelTechnique getLogicalLevelTechnique2() const { return logicalLevelTechnique2; }\n    PhysicalLevelTechnique getPhysicalLevelTechnique() const { return physicalLevelTechnique; }\n\n    std::uint32_t getNumValues() const noexcept { return numValues; }\n    std::uint32_t getByteLength() const noexcept { return byteLength; }\n\nprivate:\n    int getLogicalType() const noexcept;\n\n    friend class RleEncodedStreamMetadata;\n    friend class MortonEncodedStreamMetadata;\n    friend std::unique_ptr<StreamMetadata> decode(BufferStream&);\n    static StreamMetadata decodeInternal(BufferStream&);\n\n    PhysicalStreamType physicalStreamType;\n    std::optional<LogicalStreamType> logicalStreamType;\n    LogicalLevelTechnique logicalLevelTechnique1;\n    LogicalLevelTechnique logicalLevelTechnique2;\n    PhysicalLevelTechnique physicalLevelTechnique;\n\n    // After logical Level technique was applied -> when rle is used it is the length of the runs and values array\n    std::uint32_t numValues;\n    std::uint32_t byteLength;\n};\n\nclass RleEncodedStreamMetadata : public StreamMetadata {\npublic:\n    /**\n     * Only used for RLE encoded integer values, not boolean and byte values.\n     *\n     * @param numValues After LogicalLevelTechnique was applied -> numRuns + numValues\n     * @param runs Length of the runs array\n     * @param numRleValues Used for pre-allocating the arrays on the client for faster decoding\n     */\n    RleEncodedStreamMetadata(PhysicalStreamType physicalStreamType_,\n                             std::optional<LogicalStreamType> logicalStreamType_,\n                             LogicalLevelTechnique logicalLevelTechnique1_,\n                             LogicalLevelTechnique logicalLevelTechnique2_,\n                             PhysicalLevelTechnique physicalLevelTechnique_,\n                             unsigned numValues_,\n                             unsigned byteLength_,\n                             unsigned runs_,\n                             unsigned numRleValues_) noexcept\n        : StreamMetadata(physicalStreamType_,\n                         std::move(logicalStreamType_),\n                         logicalLevelTechnique1_,\n                         logicalLevelTechnique2_,\n                         physicalLevelTechnique_,\n                         numValues_,\n                         byteLength_),\n          runs(runs_),\n          numRleValues(numRleValues_) {}\n\n    RleEncodedStreamMetadata(StreamMetadata&& streamMetadata, unsigned runs_, unsigned numRleValues_) noexcept\n        : StreamMetadata(std::move(streamMetadata)),\n          runs(runs_),\n          numRleValues(numRleValues_) {}\n\n    RleEncodedStreamMetadata() = delete;\n\n    LogicalLevelTechnique getMetadataType() const noexcept override { return LogicalLevelTechnique::RLE; }\n\n    static RleEncodedStreamMetadata decodePartial(StreamMetadata&& streamMetadata, BufferStream& tileData) {\n        const auto [runs, numValues] = util::decoding::decodeVarints<std::uint32_t, 2>(tileData);\n        return RleEncodedStreamMetadata(std::move(streamMetadata), runs, numValues);\n    }\n\n    static RleEncodedStreamMetadata decode(BufferStream& tileData) {\n        return decodePartial(decodeInternal(tileData), tileData);\n    }\n\n    unsigned getRuns() const noexcept { return runs; }\n    unsigned getNumRleValues() const noexcept { return numRleValues; }\n\nprivate:\n    unsigned runs;\n    unsigned numRleValues;\n};\n\nclass MortonEncodedStreamMetadata : public StreamMetadata {\npublic:\n    MortonEncodedStreamMetadata(PhysicalStreamType physicalStreamType_,\n                                LogicalStreamType logicalStreamType_,\n                                LogicalLevelTechnique logicalLevelTechnique1_,\n                                LogicalLevelTechnique logicalLevelTechnique2_,\n                                PhysicalLevelTechnique physicalLevelTechnique_,\n                                unsigned numValues_,\n                                unsigned byteLength_,\n                                unsigned numBits_,\n                                unsigned coordinateShift_) noexcept\n        : StreamMetadata(physicalStreamType_,\n                         std::move(logicalStreamType_),\n                         logicalLevelTechnique1_,\n                         logicalLevelTechnique2_,\n                         physicalLevelTechnique_,\n                         numValues_,\n                         byteLength_),\n          numBits(numBits_),\n          coordinateShift(coordinateShift_) {}\n\n    MortonEncodedStreamMetadata(StreamMetadata&& streamMetadata, int numBits_, int coordinateShift_) noexcept\n        : StreamMetadata(std::move(streamMetadata)),\n          numBits(numBits_),\n          coordinateShift(coordinateShift_) {}\n\n    LogicalLevelTechnique getMetadataType() const noexcept override { return LogicalLevelTechnique::MORTON; }\n\n    static MortonEncodedStreamMetadata decodePartial(StreamMetadata&& streamMetadata, BufferStream& tileData) {\n        const auto [numBits, coordShift] = util::decoding::decodeVarints<std::uint32_t, 2>(tileData);\n        return MortonEncodedStreamMetadata(std::move(streamMetadata), numBits, coordShift);\n    }\n\n    static MortonEncodedStreamMetadata decode(BufferStream& tileData) {\n        return decodePartial(decodeInternal(tileData), tileData);\n    }\n\n    unsigned getNumBits() const noexcept { return numBits; }\n    unsigned getCoordinateShift() const noexcept { return coordinateShift; }\n\nprivate:\n    unsigned numBits;\n    unsigned coordinateShift;\n};\n\nclass PdeEncodedMetadata {};\n\n} // namespace mlt::metadata::stream\n"
  },
  {
    "path": "cpp/include/mlt/metadata/tileset.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n#include <optional>\n#include <string>\n#include <variant>\n#include <vector>\n\nnamespace mlt {\nstruct BufferStream;\n}\n\nnamespace mlt::metadata::tileset {\n\n// See https://maplibre.org/maplibre-tile-spec/specification/\nnamespace schema {\n\nenum class ColumnScope {\n    // 1:1 Mapping of property and feature -> id and geometry\n    FEATURE = 0,\n    // For M-Values -> 1:1 Mapping for property and vertex\n    VERTEX = 1,\n};\n\nenum class ScalarType {\n    BOOLEAN = 0,\n    INT_8 = 1,\n    UINT_8 = 2,\n    INT_32 = 3,\n    UINT_32 = 4,\n    INT_64 = 5,\n    UINT_64 = 6,\n    FLOAT = 7,\n    DOUBLE = 8,\n    STRING = 9,\n};\n\nenum class ComplexType {\n    // vec2<Int32> for the VertexBuffer stream with additional information\n    // (streams) about the topology\n    GEOMETRY = 0,\n    // vec3<Int32> for the VertexBuffer stream with additional information\n    // (streams) about the topology\n    STRUCT = 1,\n};\n\nenum class LogicalScalarType {\n    // uin32 or 64_t depending on hasLongID\n    ID = 0,\n};\n\nenum class LogicalComplexType {\n};\n\n} // namespace schema\n\nusing schema::ColumnScope;\nusing schema::ComplexType;\nusing schema::LogicalComplexType;\nusing schema::LogicalScalarType;\nusing schema::ScalarType;\n\nenum class GeometryType {\n    POINT = 0,\n    LINESTRING = 1,\n    POLYGON = 2,\n    MULTIPOINT = 3,\n    MULTILINESTRING = 4,\n    MULTIPOLYGON = 5,\n};\n\nstruct ScalarColumn {\n    std::variant<ScalarType, LogicalScalarType> type;\n    bool hasLongID = false;\n\n    bool hasPhysicalType() const { return std::holds_alternative<ScalarType>(type); }\n    bool hasLogicalType() const { return std::holds_alternative<LogicalScalarType>(type); }\n    auto& getPhysicalType() { return std::get<ScalarType>(type); }\n    auto& getPhysicalType() const { return std::get<ScalarType>(type); }\n    auto& getLogicalType() { return std::get<LogicalScalarType>(type); }\n    auto& getLogicalType() const { return std::get<LogicalScalarType>(type); }\n    bool isID() const { return hasLogicalType() && getLogicalType() == LogicalScalarType::ID; }\n};\n\n// The type tree is flattened in to a list via a pre-order traversal.\n// Represents a column if it is a root (top-level) type or a child of a nested type.\nstruct Column;\n\nstruct ComplexColumn {\n    std::variant<ComplexType, LogicalComplexType> type;\n\n    // The complex type Geometry and the logical type BINARY have no children\n    // since there layout is implicit known. RangeMap has only one child\n    // specifying the type of the value since the key is always a vec2<double>.\n    std::vector<Column> children;\n\n    bool hasChildren() const { return !children.empty(); }\n    bool hasPhysicalType() const { return std::holds_alternative<ComplexType>(type); }\n    bool hasLogicalType() const { return std::holds_alternative<LogicalComplexType>(type); }\n    auto& getPhysicalType() { return std::get<ComplexType>(type); }\n    auto& getPhysicalType() const { return std::get<ComplexType>(type); }\n    auto& getLogicalType() { return std::get<LogicalComplexType>(type); }\n    auto& getLogicalType() const { return std::get<LogicalComplexType>(type); }\n    bool isGeometry() const { return hasPhysicalType() && getPhysicalType() == ComplexType::GEOMETRY; }\n    bool isStruct() const { return hasPhysicalType() && getPhysicalType() == ComplexType::STRUCT; }\n};\n\n// Column are top-level types in the schema\nstruct Column {\n    std::string name;\n    bool nullable = false;\n    ColumnScope columnScope = ColumnScope::FEATURE;\n    std::variant<ScalarColumn, ComplexColumn> type;\n\n    bool hasScalarType() const { return std::holds_alternative<ScalarColumn>(type); }\n    bool hasComplexType() const { return std::holds_alternative<ComplexColumn>(type); }\n    auto& getScalarType() { return std::get<ScalarColumn>(type); }\n    auto& getScalarType() const { return std::get<ScalarColumn>(type); }\n    auto& getComplexType() { return std::get<ComplexColumn>(type); }\n    auto& getComplexType() const { return std::get<ComplexColumn>(type); }\n    bool isID() const { return hasScalarType() && getScalarType().isID(); }\n    bool isGeometry() const { return hasComplexType() && getComplexType().isGeometry(); }\n    bool isStruct() const { return hasComplexType() && getComplexType().isStruct(); }\n};\n\nstruct FeatureTable {\n    std::string name;\n    std::uint32_t extent;\n    std::vector<Column> columns;\n};\n\nFeatureTable decodeFeatureTable(BufferStream&);\n\n} // namespace mlt::metadata::tileset\n"
  },
  {
    "path": "cpp/include/mlt/metadata/type_map.hpp",
    "content": "#include <cassert>\n#include <cstddef>\n#include <optional>\n\n#include <mlt/metadata/tileset.hpp>\n\nnamespace mlt::metadata::type_map {\nstruct Tag0x01 {\n    using Column = metadata::tileset::Column;\n    using ScalarColumn = metadata::tileset::ScalarColumn;\n    using ComplexColumn = metadata::tileset::ComplexColumn;\n    using ScalarType = metadata::tileset::ScalarType;\n    using ComplexType = metadata::tileset::ComplexType;\n    using LogicalScalarType = metadata::tileset::LogicalScalarType;\n    using LogicalComplexType = metadata::tileset::LogicalComplexType;\n\n    /// Produces the unique type encoding for a `Column` or `Field`\n    static std::optional<std::uint32_t> encodeColumnType(std::optional<ScalarType> physicalScalarType,\n                                                         std::optional<LogicalScalarType> logicalScalarType,\n                                                         std::optional<ComplexType> physicalComplexType,\n                                                         std::optional<LogicalComplexType>,\n                                                         bool isNullable,\n                                                         bool hasChildren,\n                                                         bool hasLongIDs) {\n        if (physicalScalarType) {\n            if (!hasChildren) {\n                return mapScalarType(*physicalScalarType, isNullable);\n            }\n        } else if (logicalScalarType) {\n            if (logicalScalarType == LogicalScalarType::ID) {\n                return isNullable ? (hasLongIDs ? 3 : 2) : (hasLongIDs ? 1 : 0);\n            }\n        } else if (physicalComplexType) {\n            if (physicalComplexType == ComplexType::GEOMETRY) {\n                if (!isNullable && !hasChildren) {\n                    return 4;\n                }\n            } else if (physicalComplexType == ComplexType::STRUCT) {\n                if (!isNullable && hasChildren) {\n                    return 30;\n                }\n            }\n        }\n        return std::nullopt;\n    }\n\n    /// Re-create a `Column` from the unique type code.\n    /// The inverse of `encodeColumnType`\n    static std::optional<Column> decodeColumnType(std::uint32_t typeCode) {\n        using ColumnScope = metadata::tileset::ColumnScope;\n        switch (typeCode) {\n            case 0:\n            case 1:\n            case 2:\n            case 3:\n                return Column{.name = {},\n                              .nullable = (typeCode & 1) != 0,\n                              .columnScope = ColumnScope::FEATURE,\n                              .type = ScalarColumn{.type = LogicalScalarType::ID, .hasLongID = typeCode > 1}};\n            case 4:\n                return Column{.name = {},\n                              .nullable = false,\n                              .columnScope = ColumnScope::FEATURE,\n                              .type = ComplexColumn{.type = ComplexType::GEOMETRY, .children = {}}};\n            case 30:\n                return Column{.name = {},\n                              .nullable = false,\n                              .columnScope = ColumnScope::FEATURE,\n                              .type = ComplexColumn{.type = ComplexType::STRUCT, .children = {}}};\n            default:\n                if (const auto type = mapScalarType(typeCode); type) {\n                    return Column{.name = {},\n                                  .nullable = (typeCode & 1) != 0,\n                                  .columnScope = ColumnScope::FEATURE,\n                                  .type = ScalarColumn{.type = *type}};\n                }\n                return std::nullopt;\n        }\n    }\n\n    static bool columnTypeHasName(std::uint32_t typeCode) { return (10 <= typeCode); }\n\n    static bool columnTypeHasChildren(std::uint32_t typeCode) { return (typeCode == 30); }\n\n    static bool hasStreamCount(const Column& column) {\n        if (column.hasScalarType()) {\n            if (column.getScalarType().hasPhysicalType()) {\n                switch (column.getScalarType().getPhysicalType()) {\n                    default:\n                    case ScalarType::BOOLEAN:\n                    case ScalarType::INT_8:\n                    case ScalarType::UINT_8:\n                    case ScalarType::INT_32:\n                    case ScalarType::UINT_32:\n                    case ScalarType::INT_64:\n                    case ScalarType::UINT_64:\n                    case ScalarType::FLOAT:\n                    case ScalarType::DOUBLE:\n                        return false;\n                    case ScalarType::STRING:\n                        return true;\n                };\n            } else if (column.getScalarType().hasLogicalType()) {\n                if (column.getScalarType().getLogicalType() == LogicalScalarType::ID) {\n                    return false;\n                }\n            }\n        } else if (column.hasComplexType()) {\n            if (column.getComplexType().hasPhysicalType()) {\n                switch (column.getComplexType().getPhysicalType()) {\n                    case ComplexType::GEOMETRY:\n                    case ComplexType::STRUCT:\n                        return true;\n                    default:\n                        break;\n                }\n            }\n        }\n        // other cases should be impossible\n        assert(false);\n        return false;\n    }\n\nprivate:\n    constexpr static std::optional<ScalarType> mapScalarType(std::uint32_t typeCode) {\n        switch (typeCode) {\n            case 10:\n            case 11:\n                return ScalarType::BOOLEAN;\n            case 12:\n            case 13:\n                return ScalarType::INT_8;\n            case 14:\n            case 15:\n                return ScalarType::UINT_8;\n            case 16:\n            case 17:\n                return ScalarType::INT_32;\n            case 18:\n            case 19:\n                return ScalarType::UINT_32;\n            case 20:\n            case 21:\n                return ScalarType::INT_64;\n            case 22:\n            case 23:\n                return ScalarType::UINT_64;\n            case 24:\n            case 25:\n                return ScalarType::FLOAT;\n            case 26:\n            case 27:\n                return ScalarType::DOUBLE;\n            case 28:\n            case 29:\n                return ScalarType::STRING;\n            default:\n                return std::nullopt;\n        }\n    }\n    constexpr static std::optional<std::uint32_t> mapScalarType(ScalarType typeCode, bool isNullable) {\n        switch (typeCode) {\n            case ScalarType::BOOLEAN:\n                return isNullable ? 11 : 10;\n            case ScalarType::INT_8:\n                return isNullable ? 13 : 12;\n            case ScalarType::UINT_8:\n                return isNullable ? 15 : 14;\n            case ScalarType::INT_32:\n                return isNullable ? 17 : 16;\n            case ScalarType::UINT_32:\n                return isNullable ? 19 : 18;\n            case ScalarType::INT_64:\n                return isNullable ? 21 : 20;\n            case ScalarType::UINT_64:\n                return isNullable ? 23 : 22;\n            case ScalarType::FLOAT:\n                return isNullable ? 25 : 24;\n            case ScalarType::DOUBLE:\n                return isNullable ? 27 : 26;\n            case ScalarType::STRING:\n                return isNullable ? 29 : 28;\n            default:\n                assert(false);\n                return std::nullopt;\n        }\n    }\n};\n} // namespace mlt::metadata::type_map\n"
  },
  {
    "path": "cpp/include/mlt/polyfill.hpp",
    "content": "#pragma once\n\n#include <algorithm>\n#include <bit>\n#include <ranges> // IWYU pragma: keep - Needed by MSVC\n#include <utility>\n\nnamespace std {\n#if !__has_cpp_attribute(__cpp_lib_to_underlying)\ntemplate <typename E>\nconstexpr auto to_underlying(E e) noexcept {\n    return static_cast<std::underlying_type_t<E>>(e);\n}\n#endif\n\n#if !__has_cpp_attribute(__cpp_lib_byteswap)\ntemplate <std::integral T>\nconstexpr T byteswap(T value) noexcept {\n    static_assert(std::has_unique_object_representations_v<T>, \"T may not have padding bits\");\n    auto value_representation = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);\n    std::ranges::reverse(value_representation);\n    return std::bit_cast<T>(value_representation);\n}\n#endif\n} // namespace std\n"
  },
  {
    "path": "cpp/include/mlt/projection.hpp",
    "content": "#pragma once\n\n#include <mlt/coordinate.hpp>\n#include <mlt/layer.hpp>\n\n#include <cmath>\n#include <cstdint>\n#include <numbers>\n#include <stdexcept>\n\nnamespace mlt {\n\n// Intended to match the results of\n// https://github.com/mapbox/vector-tile-js/blob/77851380b63b07fd0af3d5a3f144cc86fb39fdd1/lib/vectortilefeature.js#L129\n\nclass Projection {\npublic:\n    Projection() = delete;\n    Projection(const Projection&) noexcept = default;\n    Projection(Projection&&) noexcept = default;\n    Projection(Layer::extent_t extent, const TileCoordinate& tile)\n        : size(extent * (1 << tile.z)),\n          x0(extent * tile.x),\n          y0(extent * tile.y),\n          s1(360.0 / size) {\n        if (extent == 0) {\n            throw std::runtime_error(\"Invalid tile extent\");\n        }\n    }\n\n    Coordinate project(const Coordinate& coord) const noexcept { return {projectX(coord.x), projectY(coord.y)}; }\n\nprivate:\n    float projectX(float x) const noexcept { return ((x + x0) * s1) - 180.0f; }\n    float projectY(float y) const noexcept {\n        return (2.0f * radToDeg(std::atan(std::exp(degToRad(180.0f - ((y + y0) * s1)))))) - 90.0f;\n    }\n\n    static float degToRad(float deg) noexcept { return deg * std::numbers::pi / 180.0; }\n    static float radToDeg(float rad) noexcept { return rad * 180 / std::numbers::pi; }\n\n    std::uint64_t size;\n    std::uint64_t x0;\n    std::uint64_t y0;\n    double s1;\n    constexpr static double s2 = 360.0 / std::numbers::pi;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/properties.hpp",
    "content": "#pragma once\n\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/util/packed_bitset.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <cstdint>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\nnamespace mlt {\n\n/// A block of data and a collection of strings views on it\nclass StringDictViews : util::noncopyable {\npublic:\n    StringDictViews() = default;\n    StringDictViews(std::vector<std::uint8_t>&& data_, std::vector<std::string_view> views_) noexcept\n        : data(std::move(data_)),\n          views(std::move(views_)) {}\n    StringDictViews(std::shared_ptr<std::vector<std::uint8_t>> data_, std::vector<std::string_view> views_) noexcept\n        : sharedData(std::move(data_)),\n          views(std::move(views_)) {}\n    StringDictViews(StringDictViews&&) noexcept = default;\n    StringDictViews& operator=(StringDictViews&&) = default;\n\n    const auto& getStrings() const noexcept { return views; }\n\nprivate:\n    std::vector<std::uint8_t> data;\n    std::shared_ptr<std::vector<std::uint8_t>> sharedData;\n    std::vector<std::string_view> views;\n};\n\n/// A single feature property.\n/// String properties reference the source property vector and must not outlive it.\nusing Property = std::variant<std::nullptr_t,\n                              bool,\n                              std::optional<bool>,\n                              std::int32_t,\n                              std::optional<std::int32_t>,\n                              std::int64_t,\n                              std::optional<std::int64_t>,\n                              std::uint32_t,\n                              std::optional<std::uint32_t>,\n                              std::uint64_t,\n                              std::optional<std::uint64_t>,\n                              float,\n                              std::optional<float>,\n                              double,\n                              std::optional<double>,\n                              std::string_view>;\n\n/// Map of properties for a single feature\nusing PropertyMap = std::unordered_map<std::string, Property>;\n\n/// A single property for a column, with one value per feature\nusing PropertyVec = std::variant<std::vector<std::uint8_t>,\n                                 std::vector<std::uint32_t>,\n                                 std::vector<std::uint64_t>,\n                                 std::vector<std::int32_t>,\n                                 std::vector<std::int64_t>,\n                                 std::vector<float>,\n                                 std::vector<double>,\n                                 StringDictViews>;\n\nnamespace detail {\nstruct PropertyCounter {\n    const bool byteIsBoolean;\n    template <typename T>\n    std::size_t operator()(const std::vector<T>& vec) const noexcept {\n        return vec.size();\n    }\n    std::size_t operator()(const std::vector<std::uint8_t>& vec) const noexcept {\n        // For boolean columns, each bit is a property\n        return vec.size() * (byteIsBoolean ? 8 : 1);\n    }\n    std::size_t operator()(const StringDictViews& views) const noexcept { return views.getStrings().size(); }\n};\n} // namespace detail\nstatic inline std::size_t propertyCount(const PropertyVec& vec, bool byteIsBoolean) {\n    return std::visit(detail::PropertyCounter{byteIsBoolean}, vec);\n}\n\n/// A column of properties and the present bits for each feature\nclass PresentProperties : public util::noncopyable {\npublic:\n    using ScalarType = metadata::tileset::ScalarType;\n\n    PresentProperties() = delete;\n    PresentProperties(ScalarType type_, PropertyVec properties_, const PackedBitset& present) noexcept;\n\n    ScalarType getType() const noexcept { return type; }\n    bool isBoolean() const noexcept { return type == ScalarType::BOOLEAN; }\n    const PropertyVec& getProperties() const noexcept { return properties; }\n\n    std::size_t getPropertyCount() const { return propertyCount(properties, isBoolean()); }\n\n    std::optional<Property> getProperty(std::uint32_t logicalIndex) const;\n\nprivate:\n    ScalarType type;\n    PropertyVec properties;\n\n    using ByteIndexVec = std::vector<std::uint8_t>;\n    using ShortIndexVec = std::vector<std::uint16_t>;\n    using IntIndexVec = std::vector<std::uint32_t>;\n    std::variant<std::monostate, ByteIndexVec, ShortIndexVec, IntIndexVec> physicalIndexes;\n};\n\n/// All the property columns for a layer\nusing PropertyVecMap = std::unordered_map<std::string, PresentProperties>;\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/tile.hpp",
    "content": "#pragma once\n\n#include <mlt/layer.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <vector>\n\nnamespace mlt {\n\nclass MapLibreTile : public util::noncopyable {\npublic:\n    MapLibreTile() = delete;\n    MapLibreTile(std::vector<Layer> layers_) noexcept\n        : layers(std::move(layers_)) {}\n    MapLibreTile(MapLibreTile&&) noexcept = default;\n    MapLibreTile& operator=(MapLibreTile&&) noexcept = default;\n\n    const std::vector<Layer>& getLayers() const noexcept { return layers; }\n\n    const Layer* getLayer(const std::string_view& name) const noexcept {\n        auto hit = std::ranges::find_if(layers, [&](const auto& layer) { return layer.getName() == name; });\n        return (hit != layers.end()) ? &*hit : nullptr;\n    }\n\nprivate:\n    std::vector<Layer> layers;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/util/buffer_stream.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <cstdint>\n#include <cstring>\n#include <stdexcept>\n\nnamespace mlt {\n\nstruct BufferStream : public util::noncopyable {\n    BufferStream() = delete;\n    BufferStream(DataView data_) noexcept\n        : data(std::move(data_)),\n          offset(0) {}\n    BufferStream(BufferStream&&) = delete;\n    BufferStream& operator=(BufferStream&&) = delete;\n\n    auto getSize() const noexcept { return data.size(); }\n    auto getOffset() const noexcept { return offset; }\n    auto getRemaining() const noexcept { return data.size() - offset; }\n    bool available(std::size_t size = 1) const noexcept { return size <= getRemaining(); }\n\n    /// Get another DataView representing a portion of the remaining data\n    BufferStream getSubStream(std::size_t offset, std::size_t length) const {\n        const auto remaining = getRemaining();\n        if (offset + length > remaining) {\n            throw std::runtime_error(\"Substream exceeds buffer size\");\n        }\n        return {{getReadPosition<DataView::value_type>() + offset, length}};\n    }\n\n    void reset() { offset = 0; }\n    void reset(DataView data_) {\n        data = std::move(data_);\n        offset = 0;\n    }\n\n    template <typename T = std::uint8_t>\n    const T* getData() const noexcept {\n        return reinterpret_cast<const T*>(data.data());\n    }\n    template <typename T = std::uint8_t>\n    const T* getReadPosition() const noexcept {\n        return reinterpret_cast<const T*>(&data[offset]);\n    }\n\n    template <typename T = std::uint8_t>\n    DataView::value_type read() {\n        check(sizeof(T));\n        const T* p = getReadPosition<T>();\n        consume(sizeof(T));\n        return static_cast<DataView::value_type>(*p);\n    }\n\n    void read(void* buffer, std::size_t size) {\n        check(size);\n        std::memcpy(buffer, getReadPosition(), size);\n        consume(size);\n    }\n\n    template <typename T = std::uint8_t>\n    DataView::value_type peek() const {\n        check(sizeof(T));\n        return *getReadPosition<T>();\n    }\n\n    void consume(std::size_t count) {\n        check(count);\n        offset += count;\n    }\n\nprivate:\n    void check(std::size_t count) const {\n        if (!available(count)) {\n            throw std::runtime_error(\"Unexpected end of buffer\");\n        }\n    }\n\n    DataView data;\n    std::size_t offset;\n};\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/util/noncopyable.hpp",
    "content": "#pragma once\n\nnamespace mlt::util {\n\nclass noncopyable {\npublic:\n    noncopyable(noncopyable&) = delete;\n    noncopyable(noncopyable&&) = default;\n    noncopyable& operator=(const noncopyable&) = delete;\n    noncopyable& operator=(noncopyable&&) noexcept = default;\n\nprotected:\n    constexpr noncopyable() noexcept = default;\n    ~noncopyable() noexcept = default;\n};\n\n} // namespace mlt::util\n"
  },
  {
    "path": "cpp/include/mlt/util/packed_bitset.hpp",
    "content": "#pragma once\n\n#include <bit>\n#include <cassert>\n#include <cstdint>\n#include <numeric>\n#include <optional>\n#include <vector>\n\nnamespace mlt {\n\n// `std::vector<bool>` is not great, just use a vector of bytes\nusing PackedBitset = std::vector<std::uint8_t>;\n\n/// Test a specific bit\nstatic inline bool testBit(const PackedBitset& bitset, std::size_t i) noexcept {\n    return ((i / 8) < bitset.size()) && (bitset[i / 8] & (1 << (i % 8)));\n}\n\n/// Get the total number of bits set\nstatic inline std::size_t countSetBits(const PackedBitset& bitset) {\n    // NOLINTNEXTLINE(boost-use-ranges)\n    return std::accumulate(\n        bitset.begin(), bitset.end(), static_cast<std::size_t>(0), [](const auto total, const auto byte) {\n            return total + static_cast<unsigned>(std::popcount(byte));\n        });\n}\n\n/// Return the index of the next set bit within the bitstream\n/// @param bits The bitset\n/// @param afterIndex The bit index to start with\n/// @return The index of the next set bit (including the starting index)\nstatic inline std::optional<std::size_t> nextSetBit(const PackedBitset& bits,\n                                                    const std::size_t afterIndex = 0) noexcept {\n    if (std::size_t byteIndex = (afterIndex / 8); byteIndex < bits.size()) {\n        auto byte = bits[byteIndex];\n\n        // If we're mid-byte, shift it down so the next bit is in the 1 position\n        std::size_t result = afterIndex;\n        if (const auto partialBits = result & 7; partialBits) {\n            byte >>= partialBits;\n            if (!byte) {\n                // skip to the next byte\n                if (++byteIndex == bits.size()) {\n                    return {};\n                }\n                result += (8 - partialBits);\n                byte = bits[byteIndex];\n            }\n        }\n\n        while (byteIndex < bits.size()) {\n            // If this byte is non-zero, the next bit is within it\n            if (byte) {\n                const auto ffs = std::countr_zero(byte);\n                return result + ffs;\n            }\n            // Continue to the next byte\n            if (++byteIndex < bits.size()) {\n                byte = bits[byteIndex];\n                result += 8;\n            }\n        }\n    }\n    return {};\n}\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/include/mlt/util/stl.hpp",
    "content": "#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <vector>\n\nnamespace mlt::util {\n\n/// Create a vector of N items by invoking the given function N times\ntemplate <typename T, typename F, typename I = std::size_t>\n    requires requires(F f, I i) {\n        { f(i) } -> std::same_as<T>;\n    }\nstd::vector<T> generateVector(const std::size_t count, F generator) {\n    std::vector<T> result;\n    result.reserve(count);\n    std::generate_n(\n        std::back_inserter(result), count, [i = I{0}, f = std::move(generator)]() mutable { return f(i++); });\n    return result;\n}\n\n// Helper for using lambdas with `std::variant`\n// See https://en.cppreference.com/w/cpp/utility/variant/visit\ntemplate <class... Ts>\nstruct overloaded : Ts... {\n    using Ts::operator()...;\n};\n\n// explicit deduction guide (not needed as of C++20)\n// (but seems to be needed by MSVC)\ntemplate <class... Ts>\noverloaded(Ts...) -> overloaded<Ts...>;\n\n} // namespace mlt::util\n"
  },
  {
    "path": "cpp/include/mlt/util/varint.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/util/buffer_stream.hpp>\n\n#include <stdexcept>\n#include <type_traits>\n\nnamespace mlt::util::decoding {\n\n/// Returns the size of the varint encoding for a given value.\n/// Equivalent to `max(1,ceil(log128(value)))`\ntemplate <typename T = std::uint32_t>\nstd::size_t getVarintSize(T value) {\n    return std::max<std::size_t>(1, ((8 * sizeof(value)) - std::countl_zero(value) + 6) / 7);\n}\n\ntemplate <typename T>\n    requires(std::is_integral_v<T>)\nT decodeVarint(BufferStream&);\n\n// allow decoding directly to enum types\ntemplate <typename T>\n    requires(std::is_enum_v<T>)\nT decodeVarint(BufferStream& tileData) {\n    return static_cast<T>(decodeVarint<std::make_unsigned_t<std::underlying_type_t<T>>>(tileData));\n}\n\ntemplate <>\ninline std::uint32_t decodeVarint(BufferStream& buffer) {\n    // Max 4 bytes supported\n    auto b = buffer.read<std::uint8_t>();\n    auto value = static_cast<std::uint32_t>(b & 0x7f);\n    if (b & 0x80) {\n        b = buffer.read<std::uint8_t>();\n        value |= static_cast<std::uint32_t>(b & 0x7f) << 7;\n        if (b & 0x80) {\n            b = buffer.read<std::uint8_t>();\n            value |= static_cast<std::uint32_t>(b & 0x7f) << 14;\n            if (b & 0x80) {\n                b = buffer.read<std::uint8_t>();\n                value |= static_cast<std::uint32_t>(b & 0x7f) << 21;\n                if (b & 0x80) {\n                    const auto v = static_cast<std::uint32_t>(buffer.read<std::uint8_t>() & 0x7f);\n                    if (v > 0x0f) {\n                        throw std::runtime_error(\"varint exceeds 32 bits\");\n                    }\n                    value |= v << 28;\n                }\n            }\n        }\n    }\n    return value;\n}\n\ntemplate <>\ninline std::uint64_t decodeVarint(BufferStream& buffer) {\n    std::uint64_t value = 0;\n    for (int shift = 0; buffer.available();) {\n        auto b = buffer.read<std::uint8_t>();\n        value |= static_cast<std::uint64_t>(b & 0x7F) << shift;\n\n        if (shift == 63 && b > 1) {\n            throw std::runtime_error(\"Varint too long\");\n        }\n        if ((b & 0x80) == 0) {\n            break;\n        }\n\n        shift += 7;\n        if (shift >= 64) {\n            throw std::runtime_error(\"Varint too long\");\n        }\n    }\n    return value;\n}\n\n/// Decode N varints, retrurning the values in a `std::tuple`\ntemplate <typename T, std::size_t N>\n    requires(std::is_integral_v<T> || std::is_enum_v<T>, 0 < N)\nauto decodeVarints(BufferStream& buffer) {\n    auto v = std::make_tuple(static_cast<T>(decodeVarint<std::uint32_t>(buffer)));\n    if constexpr (N == 1) {\n        return v;\n    } else\n        return std::tuple_cat(std::move(v), decodeVarints<T, N - 1>(buffer));\n}\n\n/// Decode N varints into the provided buffer\n/// Each result is cast to the target type.\ntemplate <typename TDecode, typename TTarget = TDecode>\n    requires(std::is_integral_v<TDecode> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>) &&\n             sizeof(TDecode) <= sizeof(TTarget))\nvoid decodeVarints(BufferStream& buffer, const std::uint32_t numValues, TTarget* out) {\n    std::generate_n(out, numValues, [&buffer]() { return static_cast<TTarget>(decodeVarint<TDecode>(buffer)); });\n}\n\n} // namespace mlt::util::decoding\n"
  },
  {
    "path": "cpp/mod.just",
    "content": "just := quote(just_executable())\n\n_default: (just '--list' 'cpp')\n\n[private]\njust *args:\n    {{just}} {{args}}\n\n# Initialize CMake build\ncmake-init:\n    cmake -B build -S . -DCMAKE_BUILD_TYPE=Coverage\n\n# Build with CMake\ncmake-build: cmake-init\n    cmake --build build --target mlt-cpp-test mlt-cpp-json\n\n# Quick compile check (CMake build)\ncheck: cmake-build\n\n# Run all CI steps: test, coverage, bazel build\nci-test: coverage bazel-build\n    {{just}} assert-git-is-clean\n\n# Delete build artifacts\nclean:\n    rm -rf build\n\n# Reformat code\nfmt:\n    pre-commit run --all-files clang-format\n\n# Run linting\nlint:\n    echo \"TODO: Add C++ linting command\"\n\n_install_npm_deps:\n    cd ../test/synthetic/synthetic-test-utils && npm ci\n    npm ci\n\n_test-synthetic: _install_npm_deps\n    {{just}} ts install\n    npx vitest run tool/synthetic.test.ts\n\n# Run tests\n[working-directory: 'build']\ntest: _clean_coverage_data cmake-build _test-synthetic\n    ctest\n\n_clean_coverage_data:\n    find . -name \"*.gcda\" -delete\n\n# Generate coverage report\n[working-directory: 'build']\ncoverage: _clean_coverage_data (_check-tool 'gcovr') test\n    gcovr --root ../.. \\\n        --filter ../src --filter ../include \\\n        --txt coverage.txt \\\n        --merge-mode-functions=separate \\\n        --cobertura-pretty --cobertura coverage.xml \\\n        --html-details coverage.html\n    @echo \"Coverage report at $PWD/coverage.html\"\n\n# Build with Bazel\nbazel-build:\n    bazel build //cpp:mlt_cpp\n\n# Sanity test for mlt-cpp-json tool\ntest-json:\n    build/tool/mlt-cpp-json ../test/expected/tag0x01/bing/4-12-6.mlt | jq . >/dev/null\n\n_check-tool command:\n    @which {{command}} > /dev/null || (echo \"Error: {{command}} is not installed. Please install it using: brew install {{command}} (macOS) or pip3 install {{command}} (Linux)\" && exit 1)\n"
  },
  {
    "path": "cpp/package.json",
    "content": "{\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"synthetic-test-utils\": \"file:../test/synthetic/synthetic-test-utils\",\n    \"vitest\": \"^4.0.18\"\n  }\n}\n"
  },
  {
    "path": "cpp/src/mlt/decode/geometry.hpp",
    "content": "#pragma once\n\n#include <mlt/decode/int.hpp>\n#include <mlt/feature.hpp>\n#include <mlt/geometry.hpp>\n#include <mlt/geometry_vector.hpp>\n#include <mlt/metadata/stream.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <stdexcept>\n#include <string>\n#include <utility>\n#include <vector>\n\nnamespace mlt::decoder {\n\nclass GeometryDecoder {\npublic:\n    using GeometryVector = geometry::GeometryVector;\n\n    GeometryDecoder(IntegerDecoder& intDecoder)\n        : intDecoder(intDecoder) {}\n\nprivate:\n    enum class VectorType : std::uint32_t {\n        FLAT,\n        CONST,\n        SEQUENCE,\n        DICTIONARY,\n        FSST_DICTIONARY,\n    };\n\n    VectorType getVectorTypeIntStream(const metadata::stream::StreamMetadata& streamMetadata) {\n        using namespace metadata::stream;\n        const auto logicalLevelTechnique1 = streamMetadata.getLogicalLevelTechnique1();\n        const auto logicalLevelTechnique2 = streamMetadata.getLogicalLevelTechnique2();\n        const auto metadataType = streamMetadata.getMetadataType();\n        const auto& rleMetadata = static_cast<const RleEncodedStreamMetadata&>(streamMetadata);\n        const auto rleRuns = (metadataType == LogicalLevelTechnique::RLE) ? rleMetadata.getRuns() : 0;\n\n        if (logicalLevelTechnique1 == LogicalLevelTechnique::RLE) {\n            assert(metadataType == LogicalLevelTechnique::RLE);\n            return (rleRuns == 1) ? VectorType::CONST : VectorType::FLAT;\n        } else if (logicalLevelTechnique1 == LogicalLevelTechnique::DELTA &&\n                   logicalLevelTechnique2 == LogicalLevelTechnique::RLE) {\n            assert(metadataType == LogicalLevelTechnique::RLE);\n            // If base value equals delta value then one run else two runs\n            if (rleRuns == 1 || rleRuns == 2) {\n                return VectorType::SEQUENCE;\n            }\n        }\n\n        return (streamMetadata.getNumValues() == 1) ? VectorType::CONST : VectorType::FLAT;\n    }\n\npublic:\n    std::unique_ptr<GeometryVector> decodeGeometryColumn(BufferStream& tileData,\n                                                         const metadata::tileset::Column& column,\n                                                         std::uint32_t numStreams) {\n        using namespace util::decoding;\n        using namespace metadata::stream;\n        using namespace metadata::tileset;\n\n        std::vector<GeometryType> geometryTypes;\n        std::vector<std::uint32_t> geometryOffsets;\n        std::vector<std::uint32_t> partOffsets;\n        std::vector<std::uint32_t> ringOffsets;\n        std::vector<std::uint32_t> vertexOffsets;\n        std::vector<std::uint32_t> indexBuffer;\n        std::vector<std::uint32_t> triangles;\n        std::vector<std::int32_t> vertices;\n\n        const auto geomTypeMetadata = StreamMetadata::decode(tileData);\n        if (!geomTypeMetadata) {\n            throw std::runtime_error(\"geometry column missing metadata stream: \" + column.name);\n        }\n\n        [[maybe_unused]] const auto geometryTypesVectorType = getVectorTypeIntStream(*geomTypeMetadata);\n\n        // If all geometries in the column have the same geometry type, we could decode them\n        // somewhat more efficiently, and return the geometry in a more GPU-friendly form.\n        // if (geometryTypesVectorType == VectorType::CONST) {\n        //     const auto geomType = intDecoder.decodeConstIntStream<std::uint32_t, std::uint32_t, GeometryType>(\n        //         tileData, *geomTypeMetadata);\n        //     ...\n        // }\n\n        // Different geometry types are mixed in the geometry column\n        intDecoder.decodeIntStream<std::uint32_t, std::uint32_t, GeometryType>(\n            tileData, geometryTypes, *geomTypeMetadata);\n\n        std::optional<geometry::MortonSettings> mortonSettings;\n\n        for (std::uint32_t i = 1; i < numStreams; ++i) {\n            const auto geomStreamMetadata = StreamMetadata::decode(tileData);\n\n            switch (geomStreamMetadata->getPhysicalStreamType()) {\n                case PhysicalStreamType::LENGTH: {\n                    if (!geomStreamMetadata->getLogicalStreamType() ||\n                        !geomStreamMetadata->getLogicalStreamType()->getLengthType()) {\n                        throw std::runtime_error(\"Length stream missing logical type: \" + column.name);\n                    }\n                    const auto type = *geomStreamMetadata->getLogicalStreamType()->getLengthType();\n                    std::optional<std::reference_wrapper<std::vector<std::uint32_t>>> target;\n                    switch (type) {\n                        case LengthType::GEOMETRIES:\n                            target = geometryOffsets;\n                            break;\n                        case LengthType::PARTS:\n                            target = partOffsets;\n                            break;\n                        case LengthType::RINGS:\n                            target = ringOffsets;\n                            break;\n                        case LengthType::TRIANGLES:\n                            target = triangles;\n                            break;\n                        default:\n                            throw std::runtime_error(\"Length stream type '\" + std::to_string(std::to_underlying(type)) +\n                                                     \" not implemented: \" + column.name);\n                    }\n                    intDecoder.decodeIntStream<std::uint32_t, std::uint32_t, std::uint32_t>(\n                        tileData, target->get(), *geomStreamMetadata);\n                    break;\n                }\n                case PhysicalStreamType::OFFSET: {\n                    if (!geomStreamMetadata->getLogicalStreamType() ||\n                        !geomStreamMetadata->getLogicalStreamType()->getOffsetType()) {\n                        throw std::runtime_error(\"Offset stream missing type: \" + column.name);\n                    }\n                    const auto type = *geomStreamMetadata->getLogicalStreamType()->getOffsetType();\n                    std::optional<std::reference_wrapper<std::vector<std::uint32_t>>> target;\n                    switch (type) {\n                        case OffsetType::VERTEX:\n                            target = vertexOffsets;\n                            break;\n                        case OffsetType::INDEX:\n                            target = indexBuffer;\n                            break;\n                        default:\n                            throw std::runtime_error(\"Offset stream type '\" + std::to_string(std::to_underlying(type)) +\n                                                     \" not implemented: \" + column.name);\n                    }\n                    intDecoder.decodeIntStream<std::uint32_t>(tileData, target->get(), *geomStreamMetadata);\n                    break;\n                }\n                case PhysicalStreamType::DATA: {\n                    if (!geomStreamMetadata->getLogicalStreamType() ||\n                        !geomStreamMetadata->getLogicalStreamType()->getDictionaryType()) {\n                        throw std::runtime_error(\"Data stream missing dictionary type: \" + column.name);\n                    }\n                    if (mortonSettings) {\n                        throw std::runtime_error(\"multiple data streams\");\n                    }\n                    const auto type = *geomStreamMetadata->getLogicalStreamType()->getDictionaryType();\n                    switch (type) {\n                        case DictionaryType::VERTEX:\n                            switch (geomStreamMetadata->getPhysicalLevelTechnique()) {\n                                case PhysicalLevelTechnique::NONE:\n                                case PhysicalLevelTechnique::VARINT:\n                                case PhysicalLevelTechnique::FAST_PFOR:\n                                    intDecoder.decodeIntStream<std::uint32_t, std::uint32_t, std::int32_t>(\n                                        tileData, vertices, *geomStreamMetadata, /*isSigned=*/true);\n                                    break;\n                                default:\n                                    throw std::runtime_error(\"Unsupported encoding \" +\n                                                             std::to_string(std::to_underlying(\n                                                                 geomStreamMetadata->getPhysicalLevelTechnique())) +\n                                                             \" for geometries: \" + column.name);\n                            };\n                            break;\n                        case DictionaryType::MORTON: {\n                            assert(geomStreamMetadata->getMetadataType() == LogicalLevelTechnique::MORTON);\n                            const auto& mortonStreamMetadata = static_cast<const MortonEncodedStreamMetadata&>(\n                                *geomStreamMetadata);\n                            // TODO: This results in double-morton-decoding, need to align with TypeScript\n                            // implementation. mortonSettings = geometry::MortonSettings{\n                            //    .numBits = mortonStreamMetadata.getNumBits(),\n                            //    .coordinateShift = mortonStreamMetadata.getCoordinateShift()};\n                            intDecoder.decodeMortonStream<std::uint32_t, std::int32_t>(\n                                tileData, vertices, mortonStreamMetadata);\n                            break;\n                        }\n                        default:\n                            throw std::runtime_error(\"Dictionary type '\" + std::to_string(std::to_underlying(type)) +\n                                                     \"' not implemented: \" + column.name);\n                    }\n                    break;\n                }\n                case PhysicalStreamType::PRESENT:\n                    break;\n                default:\n                    throw std::runtime_error(\"Unsupported logical stream type: \" + column.name);\n            }\n        }\n\n        if (!indexBuffer.empty() && partOffsets.empty()) {\n            /* Case when the indices of a Polygon outline are not encoded in the data so no\n             *  topology data are present in the tile */\n            return std::make_unique<geometry::FlatGpuVector>(\n                std::move(geometryTypes), std::move(triangles), std::move(indexBuffer), std::move(vertices));\n        }\n\n        if (!geometryOffsets.empty()) {\n            auto geometryOffsetsCopy = geometryOffsets; // TODO: avoid copies\n            decodeRootLengthStream(geometryTypes,\n                                   geometryOffsetsCopy,\n                                   /*bufferId=*/GeometryType::POLYGON,\n                                   geometryOffsets);\n            if (!partOffsets.empty()) {\n                if (!ringOffsets.empty()) {\n                    auto partOffsetsCopy = partOffsets;\n                    decodeLevel1LengthStream(geometryTypes,\n                                             geometryOffsets,\n                                             partOffsetsCopy,\n                                             /*isLineStringPresent=*/false,\n                                             partOffsets);\n                    auto ringOffsetsCopy = ringOffsets;\n                    decodeLevel2LengthStream(geometryTypes, geometryOffsets, partOffsets, ringOffsetsCopy, ringOffsets);\n                } else {\n                    auto partOffsetsCopy = partOffsets;\n                    decodeLevel1WithoutRingBufferLengthStream(\n                        geometryTypes, geometryOffsets, partOffsetsCopy, partOffsets);\n                }\n            }\n        } else if (!partOffsets.empty()) {\n            auto partOffsetsCopy = partOffsets;\n            if (!ringOffsets.empty()) {\n                auto ringOffsetsCopy = ringOffsets;\n                decodeRootLengthStream(geometryTypes, partOffsetsCopy, GeometryType::LINESTRING, partOffsets);\n                decodeLevel1LengthStream(geometryTypes,\n                                         partOffsets,\n                                         ringOffsetsCopy,\n                                         /*isLineStringPresent=*/true,\n                                         ringOffsets);\n            } else {\n                decodeRootLengthStream(geometryTypes, partOffsetsCopy, GeometryType::POINT, partOffsets);\n            }\n        }\n\n        if (!indexBuffer.empty()) {\n            /* Case when the indices of a Polygon outline are encoded in the tile */\n            return std::make_unique<geometry::FlatGpuVector>(\n                std::move(geometryTypes),\n                std::move(triangles),\n                std::move(indexBuffer),\n                std::move(vertices),\n                geometry::TopologyVector(std::move(geometryOffsets), std::move(partOffsets), std::move(ringOffsets)));\n        }\n\n        return std::make_unique<geometry::FlatGeometryVector>(\n            std::move(geometryTypes),\n            geometry::TopologyVector(std::move(geometryOffsets), std::move(partOffsets), std::move(ringOffsets)),\n            std::move(vertexOffsets),\n            std::move(vertices),\n            mortonSettings ? geometry::VertexBufferType::MORTON : geometry::VertexBufferType::VEC_2,\n            mortonSettings);\n    }\n\n    /*\n     * Handle the parsing of the different topology length buffers separate not generic to reduce the\n     * branching and improve the performance\n     */\n    void decodeRootLengthStream(const std::vector<metadata::tileset::GeometryType>& geometryTypes,\n                                const std::vector<std::uint32_t>& rootLengthStream,\n                                const metadata::tileset::GeometryType bufferId,\n                                std::vector<std::uint32_t>& rootBufferOffsets) {\n        assert(&rootLengthStream != &rootBufferOffsets);\n        rootBufferOffsets.resize(geometryTypes.size() + 1);\n        std::uint32_t previousOffset = rootBufferOffsets[0] = 0;\n        std::uint32_t rootLengthCounter = 0;\n        for (std::size_t i = 0; i < geometryTypes.size(); ++i) {\n            /* Test if the geometry has and entry in the root buffer\n             * BufferId: 2 GeometryOffsets -> MultiPolygon, MultiLineString, MultiPoint\n             * BufferId: 1 PartOffsets -> Polygon\n             * BufferId: 0 PartOffsets, RingOffsets -> LineString\n             * */\n            previousOffset = rootBufferOffsets[i + 1] = previousOffset + ((geometryTypes[i] > bufferId)\n                                                                              ? rootLengthStream[rootLengthCounter++]\n                                                                              : 1);\n        }\n    }\n\n    void decodeLevel1LengthStream(const std::vector<metadata::tileset::GeometryType>& geometryTypes,\n                                  const std::vector<std::uint32_t>& rootOffsetBuffer,\n                                  const std::vector<std::uint32_t>& level1LengthBuffer,\n                                  const bool isLineStringPresent,\n                                  std::vector<std::uint32_t>& level1BufferOffsets) {\n        assert(&rootOffsetBuffer != &level1BufferOffsets);\n        assert(&level1LengthBuffer != &level1BufferOffsets);\n        using metadata::tileset::GeometryType;\n        level1BufferOffsets.resize(rootOffsetBuffer[rootOffsetBuffer.size() - 1] + 1);\n        std::uint32_t previousOffset = level1BufferOffsets[0] = 0;\n        std::uint32_t level1BufferCounter = 1;\n        std::uint32_t level1LengthBufferCounter = 0;\n        for (std::size_t i = 0; i < geometryTypes.size(); ++i) {\n            const auto geometryType = geometryTypes[i];\n            const auto numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n\n            if (geometryType == GeometryType::MULTIPOLYGON || geometryType == GeometryType::POLYGON ||\n                (isLineStringPresent &&\n                 (geometryType == GeometryType::MULTILINESTRING || geometryType == GeometryType::LINESTRING))) {\n                /* For MultiPolygon, Polygon and in some cases for MultiLineString and LineString\n                 * a value in the level1LengthBuffer exists */\n                for (std::uint32_t j = 0; j < numGeometries; ++j) {\n                    previousOffset = level1BufferOffsets[level1BufferCounter++] =\n                        previousOffset + level1LengthBuffer[level1LengthBufferCounter++];\n                }\n            } else {\n                /* For MultiPoint and Point and in some cases for MultiLineString and LineString no value in the\n                 * level1LengthBuffer exists */\n                for (std::uint32_t j = 0; j < numGeometries; j++) {\n                    level1BufferOffsets[level1BufferCounter++] = ++previousOffset;\n                }\n            }\n        }\n    }\n\n    /*\n     * Case where no ring buffer exists so no MultiPolygon or Polygon geometry is part of the buffer\n     */\n    void decodeLevel1WithoutRingBufferLengthStream(const std::vector<metadata::tileset::GeometryType>& geometryTypes,\n                                                   const std::vector<std::uint32_t>& rootOffsetBuffer,\n                                                   const std::vector<std::uint32_t>& level1LengthBuffer,\n                                                   std::vector<std::uint32_t>& level1BufferOffsets) {\n        assert(&rootOffsetBuffer != &level1BufferOffsets);\n        assert(&level1LengthBuffer != &level1BufferOffsets);\n        using metadata::tileset::GeometryType;\n        level1BufferOffsets.resize(rootOffsetBuffer[rootOffsetBuffer.size() - 1] + 1);\n        std::uint32_t previousOffset = level1BufferOffsets[0] = 0;\n        std::uint32_t level1OffsetBufferCounter = 1;\n        std::uint32_t level1LengthCounter = 0;\n        for (std::size_t i = 0; i < geometryTypes.size(); ++i) {\n            const auto geometryType = geometryTypes[i];\n            const auto numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n            if (geometryType == GeometryType::MULTILINESTRING || geometryType == GeometryType::LINESTRING) {\n                /* For MultiLineString and LineString a value in the level1LengthBuffer exists */\n                for (std::uint32_t j = 0; j < numGeometries; ++j) {\n                    previousOffset = level1BufferOffsets[level1OffsetBufferCounter++] =\n                        previousOffset + level1LengthBuffer[level1LengthCounter++];\n                }\n            } else {\n                /* For MultiPoint and Point no value in level1LengthBuffer exists */\n                for (std::uint32_t j = 0; j < numGeometries; ++j) {\n                    level1BufferOffsets[level1OffsetBufferCounter++] = ++previousOffset;\n                }\n            }\n        }\n    }\n\n    void decodeLevel2LengthStream(const std::vector<metadata::tileset::GeometryType>& geometryTypes,\n                                  const std::vector<std::uint32_t>& rootOffsetBuffer,\n                                  const std::vector<std::uint32_t>& level1OffsetBuffer,\n                                  const std::vector<std::uint32_t>& level2LengthBuffer,\n                                  std::vector<std::uint32_t>& level2BufferOffsets) {\n        assert(&rootOffsetBuffer != &level2BufferOffsets);\n        assert(&level1OffsetBuffer != &level2BufferOffsets);\n        assert(&level2LengthBuffer != &level2BufferOffsets);\n        using metadata::tileset::GeometryType;\n        level2BufferOffsets.resize(level1OffsetBuffer[level1OffsetBuffer.size() - 1] + 1);\n        std::uint32_t previousOffset = level2BufferOffsets[0] = 0;\n        std::uint32_t level1OffsetBufferCounter = 1;\n        std::uint32_t level2OffsetBufferCounter = 1;\n        std::uint32_t level2LengthBufferCounter = 0;\n        for (std::size_t i = 0; i < geometryTypes.size(); ++i) {\n            const auto geometryType = geometryTypes[i];\n            const auto numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n            if (geometryType != GeometryType::POINT && geometryType != GeometryType::MULTIPOINT) {\n                /* For MultiPolygon, MultiLineString, Polygon and LineString a value in level2LengthBuffer\n                 * exists */\n                for (std::uint32_t j = 0; j < numGeometries; ++j) {\n                    const auto numParts = level1OffsetBuffer[level1OffsetBufferCounter] -\n                                          level1OffsetBuffer[level1OffsetBufferCounter - 1];\n                    level1OffsetBufferCounter++;\n                    for (std::uint32_t k = 0; k < numParts; ++k) {\n                        previousOffset = level2BufferOffsets[level2OffsetBufferCounter++] =\n                            previousOffset + level2LengthBuffer[level2LengthBufferCounter++];\n                    }\n                }\n            } else {\n                /* For MultiPoint and Point no value in level2LengthBuffer exists */\n                for (std::uint32_t j = 0; j < numGeometries; j++) {\n                    level2BufferOffsets[level2OffsetBufferCounter++] = ++previousOffset;\n                    level1OffsetBufferCounter++;\n                }\n            }\n        }\n    }\n\nprivate:\n    IntegerDecoder& intDecoder;\n};\n\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decode/int.cpp",
    "content": "#include <algorithm>\n#include <mlt/decode/int.hpp>\n#include <mlt/decode/int_template.hpp>\n\n// from fastpfor/...\n#if MLT_WITH_FASTPFOR\n#include <compositecodec.h>\n#include <fastpfor.h>\n#include <variablebyte.h>\n#endif // MLT_WITH_FASTPFOR\n\n#include <cstdint>\n\n#ifdef _MSC_VER\n#include <array>\n#include <bitset>\n#include <intrin.h>\n#endif\n\nnamespace mlt::decoder {\n\nstruct IntegerDecoder::Impl {\n#if MLT_WITH_FASTPFOR\n    // Impl pattern to prevent FastPFOR from being an API dependency\n    FastPForLib::CompositeCodec<FastPForLib::FastPFor<8>, FastPForLib::VariableByte> codec;\n#endif // MLT_WITH_FASTPFOR\n};\n\nIntegerDecoder::IntegerDecoder([[maybe_unused]] bool enableFastPFOR)\n    : impl(std::make_unique<Impl>())\n#if MLT_WITH_FASTPFOR\n      ,\n      enableFastPFOR(enableFastPFOR)\n#endif\n{\n}\n\nIntegerDecoder::~IntegerDecoder() noexcept = default;\n\nstd::uint32_t IntegerDecoder::decodeFastPfor([[maybe_unused]] BufferStream& buffer,\n                                             [[maybe_unused]] std::uint32_t* const result,\n                                             [[maybe_unused]] const std::size_t numValues,\n                                             [[maybe_unused]] const std::size_t byteLength) {\n#if MLT_WITH_FASTPFOR\n    if (enableFastPFOR) {\n#if (defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) || defined(_M_AMD64))\n#if defined(__GNUC__) || defined(__clang__)\n        // https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html\n        if (!__builtin_cpu_supports(\"sse4.1\")) {\n            // The x86 implementation in FastPFOR requires SSE4.1\n            throw std::runtime_error(\"FastPFOR decoding requires SSE4.1 on x86 platforms\");\n        }\n#elif defined(_MSC_VER)\n        // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex\n        {\n            int cpui[4];\n            __cpuid(cpui, 0);\n            cpui[2] = 0;\n            if (cpui[0] > 0) {\n                __cpuidex(cpui, 1, 0);\n            }\n\n            if (const std::bitset<32> fn1ECX = cpui[2]; !fn1ECX[19]) {\n                // The x86 implementation in FastPFOR requires SSE4.1\n                throw std::runtime_error(\"FastPFOR decoding requires SSE4.1 on x86 platforms\");\n            }\n        }\n#endif\n#endif\n\n        const auto* inputValues = reinterpret_cast<const std::uint32_t*>(buffer.getReadPosition());\n\n        // TODO: change to little endian in the encoder?\n        const auto intLength = (byteLength + sizeof(std::uint32_t) - 1) / sizeof(std::uint32_t);\n        const auto leBuffer = getTempBuffer<std::uint32_t>(intLength);\n        std::transform(inputValues, inputValues + intLength, leBuffer.get(), [](std::uint32_t v) noexcept {\n            return std::byteswap(v);\n        });\n\n        auto resultCount = numValues;\n        impl->codec.decodeArray(leBuffer, intLength, result, resultCount);\n        buffer.consume(byteLength);\n        return static_cast<std::uint32_t>(resultCount);\n    } else {\n        throw std::runtime_error(\"FastPFOR decoding is not enabled\");\n    }\n#else\n    throw std::runtime_error(\"FastPFOR decoding is not enabled. Configure with MLT_WITH_FASTPFOR=ON\");\n#endif // MLT_WITH_FASTPFOR\n}\n\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decode/int.hpp",
    "content": "#pragma once\n\n#include <mlt/metadata/stream.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/noncopyable.hpp>\n#include <mlt/util/rle.hpp>\n#include <mlt/util/vectorized.hpp>\n#include <mlt/util/zigzag.hpp>\n\n#include <cstdint>\n#include <type_traits>\n#include <vector>\n\nnamespace mlt::decoder {\n\nclass IntegerDecoder : public util::noncopyable {\npublic:\n    using StreamMetadata = metadata::stream::StreamMetadata;\n    using MortonEncodedStreamMetadata = metadata::stream::MortonEncodedStreamMetadata;\n\n    IntegerDecoder(bool enableFastPFOR);\n    ~IntegerDecoder() noexcept;\n\n    IntegerDecoder(IntegerDecoder&&) = delete;\n    // FastPFOR classes have implicitly-deleted assignment operators.\n    // We could create new ones, if really necessary.\n    IntegerDecoder& operator=(IntegerDecoder&&) = delete;\n\n    /// Decode a buffer of integers into another, according to the encoding scheme specified by the metadata\n    /// @param values Input values\n    /// @param out Output values (should be sized according to `getIntArrayBufferSize`)\n    /// @param outCount Number of elements in the output buffer\n    /// @param metadata Stream metadata specifying the encoding details\n    template <typename T, typename TTarget = T>\n        requires((std::is_integral_v<T> || std::is_enum_v<T>) &&\n                 (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>) && sizeof(T) <= sizeof(TTarget))\n    void decodeIntArray(const T* values,\n                        std::size_t count,\n                        TTarget* out,\n                        std::size_t outCount,\n                        const StreamMetadata&,\n                        bool isSigned = std::is_signed_v<T>);\n\n    /// Decode an integer stream into the target buffer\n    /// @param tileData source data\n    /// @param out output data, automatically resized\n    /// @param metadata stream metadata specifying the encoding details\n    /// @details Uses an internal buffer for intermediate values\n    template <typename TDecode, typename TInt = TDecode, typename TTarget = TDecode>\n    void decodeIntStream(BufferStream& tileData,\n                         std::vector<TTarget>& out,\n                         const StreamMetadata&,\n                         bool isSigned = std::is_signed_v<TDecode>);\n\n    /// Decode an integer stream into the target buffer\n    /// @param tileData source data\n    /// @param buffer storage for intermediate values, automatically resized\n    /// @param out output data, automatically resized\n    /// @param metadata stream metadata specifying the encoding details\n    template <typename TDecode, typename TInt = TDecode, typename TTarget = TInt>\n    void decodeIntStream(BufferStream& tileData,\n                         TInt* buffer,\n                         std::size_t bufferSize,\n                         std::vector<TTarget>& out,\n                         const StreamMetadata&,\n                         bool isSigned = std::is_signed_v<TDecode>);\n\n    template <typename TDecode, typename TInt = TDecode, typename TTarget = TInt>\n    TTarget decodeConstIntStream(BufferStream& tileData,\n                                 const StreamMetadata&,\n                                 bool isSigned = std::is_signed_v<TDecode>);\n\n    template <typename TDecode, typename TInt = TDecode, typename TTarget = TInt, bool Delta = true>\n    void decodeMortonStream(BufferStream& tileData,\n                            std::vector<TTarget>& out,\n                            const MortonEncodedStreamMetadata& metadata);\n\n    template <typename TDecode, typename TInt = TDecode, typename TTarget = TInt, bool Delta = true>\n    void decodeMortonStream(BufferStream& tileData,\n                            TInt* buffer,\n                            std::size_t bufferSize,\n                            TTarget* out,\n                            std::size_t outCount,\n                            const MortonEncodedStreamMetadata&);\n\nprivate:\n    struct Impl;\n    std::unique_ptr<Impl> impl;\n    std::vector<std::vector<std::uint8_t>> buffer;\n    std::size_t bufferIndex = 0;\n\n#if MLT_WITH_FASTPFOR\n    bool enableFastPFOR;\n#endif\n\n    // Very simple RAII wrapper for temporary buffers.\n    // Buffers must be used only within a current method invocation.\n    struct BufWrapperBase : util::noncopyable {\n        BufWrapperBase(IntegerDecoder& decoder_, std::uint8_t* ptr_) noexcept\n            : decoder(decoder_),\n              ptr(ptr_) {\n            if (ptr) {\n                decoder.bufferIndex++;\n            }\n        }\n        BufWrapperBase(BufWrapperBase&& other) noexcept\n            : decoder(other.decoder),\n              ptr(other.ptr) {\n            other.ptr = nullptr;\n        }\n        ~BufWrapperBase() noexcept {\n            if (ptr) {\n                decoder.bufferIndex--;\n            }\n        }\n        IntegerDecoder& decoder;\n        std::uint8_t* ptr;\n    };\n    template <typename T>\n    struct BufWrapper : BufWrapperBase {\n        BufWrapper(IntegerDecoder& decoder_, std::uint8_t* ptr_) noexcept\n            : BufWrapperBase(decoder_, ptr_) {}\n        T* get() const noexcept { return reinterpret_cast<T*>(ptr); }\n        operator T*() const noexcept { return get(); }\n    };\n\n    /// Get a buffer for intermediate values, which must not be retained beyond the current method invocation\n    template <typename T>\n    BufWrapper<T> getTempBuffer(std::size_t minSize) {\n        buffer.resize(std::max(buffer.size(), bufferIndex + 1));\n        buffer[bufferIndex].resize(std::max(buffer[bufferIndex].size(), minSize * sizeof(T)));\n        return {*this, buffer[bufferIndex].data()};\n    }\n\n    /// Get the size of the buffer necessary for `decodeIntArray`\n    std::size_t getIntArrayBufferSize(const std::size_t count, const StreamMetadata&);\n\n    template <typename TDecode, typename TTarget = TDecode>\n        requires(std::is_integral_v<TDecode> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>))\n    void decodeStream(BufferStream& tileData, TTarget* out, std::size_t outSize, const StreamMetadata&);\n\n    template <typename T>\n    static void decodeRLE(const std::vector<T>& values, std::vector<T>& out, const std::uint32_t numRuns);\n\n    template <typename T>\n    static void decodeDeltaRLE(const std::vector<T>& values, std::vector<T>& out, const std::uint32_t numRuns);\n\n    /// Decode zigzag-delta values\n    /// @param values Input values\n    /// @param count Count of input values\n    /// @param out Output values, must be the same size as the input\n    /// @param outCount Count of available output values\n    /// @note Input and output may reference the same memory\n    template <typename T, typename TTarget>\n        requires(std::is_integral_v<underlying_type_t<T>> && std::is_integral_v<underlying_type_t<TTarget>> &&\n                 sizeof(T) <= sizeof(TTarget))\n    static void decodeZigZagDelta(const T* values,\n                                  const std::size_t count,\n                                  TTarget* const out,\n                                  const std::size_t outCount) noexcept;\n\n    /// Decode standard or delta Morton codes\n    /// @param data Input data\n    /// @param count Number of input values\n    /// @param out Output data, must be 2x the input size\n    /// @param outCount Number of available output values\n    template <typename TDecode, typename TTarget, bool delta>\n        requires(std::is_integral_v<TDecode> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>))\n    static void decodeMortonCodes(const TDecode* const data,\n                                  const std::size_t count,\n                                  TTarget* out,\n                                  std::size_t outCount,\n                                  int numBits,\n                                  int coordinateShift) noexcept;\n\n    std::uint32_t decodeFastPfor(BufferStream& buffer,\n                                 std::uint32_t* const result,\n                                 const std::size_t numValues,\n                                 const std::size_t byteLength);\n};\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decode/int_template.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/decode/int.hpp>\n#include <mlt/util/morton_curve.hpp>\n\n#include <cassert>\n#include <span>\n#include <stdexcept>\n#include <string>\n#include <utility>\n\nnamespace mlt::decoder {\n\n#pragma region Public\n\ninline std::size_t IntegerDecoder::getIntArrayBufferSize(const std::size_t count,\n                                                         const StreamMetadata& streamMetadata) {\n    using namespace metadata::stream;\n    switch (streamMetadata.getLogicalLevelTechnique1()) {\n        case LogicalLevelTechnique::DELTA:\n            if (streamMetadata.getLogicalLevelTechnique2() == LogicalLevelTechnique::RLE &&\n                streamMetadata.getMetadataType() == LogicalLevelTechnique::RLE) {\n                const auto& rleMetadata = static_cast<const RleEncodedStreamMetadata&>(streamMetadata);\n                return rleMetadata.getNumRleValues();\n            }\n            return streamMetadata.getNumValues();\n        case LogicalLevelTechnique::NONE:\n        case LogicalLevelTechnique::COMPONENTWISE_DELTA:\n            return count;\n        case LogicalLevelTechnique::RLE: {\n            if (streamMetadata.getMetadataType() != LogicalLevelTechnique::RLE) {\n                return 0;\n            }\n            const auto& rleMetadata = static_cast<const RleEncodedStreamMetadata&>(streamMetadata);\n            return rleMetadata.getNumRleValues();\n        }\n        case LogicalLevelTechnique::MORTON: {\n            return 2 * count;\n        }\n        case LogicalLevelTechnique::PSEUDODECIMAL:\n        default:\n            return 0;\n    }\n}\n\ntemplate <typename T, typename TTarget>\n    requires((std::is_integral_v<T> || std::is_enum_v<T>) && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>) &&\n             sizeof(T) <= sizeof(TTarget))\nvoid IntegerDecoder::decodeIntArray(const T* values,\n                                    const std::size_t count,\n                                    TTarget* out,\n                                    const std::size_t outCount,\n                                    const StreamMetadata& streamMetadata,\n                                    const bool isSigned) {\n    using namespace metadata::stream;\n    using namespace util::decoding;\n    switch (streamMetadata.getLogicalLevelTechnique1()) {\n        case LogicalLevelTechnique::NONE: {\n            assert(count <= outCount);\n            const auto f = isSigned ? [](T x) noexcept { return static_cast<TTarget>(decodeZigZag(x)); } : [](T x) noexcept { return static_cast<TTarget>(x); };\n            std::transform(values, values + count, out, f);\n            return;\n        }\n        case LogicalLevelTechnique::DELTA:\n            if (streamMetadata.getLogicalLevelTechnique2() == LogicalLevelTechnique::RLE) {\n                if (streamMetadata.getMetadataType() != LogicalLevelTechnique::RLE) {\n                    throw std::runtime_error(\"invalid RLE metadata\");\n                }\n                const auto& rleMetadata = static_cast<const RleEncodedStreamMetadata&>(streamMetadata);\n                assert(outCount >= rleMetadata.getNumRleValues());\n                rle::decodeInt<T, TTarget>(values, count, out, outCount, rleMetadata.getRuns());\n                decodeZigZagDelta(out, outCount, out, outCount);\n            } else {\n                assert(outCount >= count);\n                decodeZigZagDelta(values, count, out, outCount);\n            }\n            break;\n        case LogicalLevelTechnique::COMPONENTWISE_DELTA:\n            if constexpr (std::is_same_v<TTarget, std::int32_t> || std::is_same_v<TTarget, std::uint32_t>) {\n                assert(count <= outCount);\n                std::transform(values, values + count, out, [](auto x) { return static_cast<TTarget>(x); });\n                vectorized::decodeComponentwiseDeltaVec2(out, outCount);\n                break;\n            }\n            throw std::runtime_error(\"Logical level technique COMPONENTWISE_DELTA not implemented for 64-bit values\");\n        case LogicalLevelTechnique::RLE: {\n            if (streamMetadata.getMetadataType() != LogicalLevelTechnique::RLE) {\n                throw std::runtime_error(\"invalid RLE metadata\");\n            }\n            const auto& rleMetadata = static_cast<const RleEncodedStreamMetadata&>(streamMetadata);\n            assert(rleMetadata.getNumRleValues() <= outCount);\n            auto decoder = isSigned ? [](T x) {\n                    return static_cast<TTarget>(decodeZigZag(x));\n            } : [](T x) {\n                    return static_cast<TTarget>(x);\n            };\n            rle::decodeInt<T, TTarget>(values, count, out, outCount, rleMetadata.getRuns(), std::move(decoder));\n            break;\n        }\n        case LogicalLevelTechnique::MORTON: {\n            // TODO: zig-zag decode when morton second logical level technique\n            if (streamMetadata.getMetadataType() != LogicalLevelTechnique::MORTON) {\n                throw std::runtime_error(\"invalid RLE metadata\");\n            }\n            const auto& mortonMetadata = static_cast<const MortonEncodedStreamMetadata&>(streamMetadata);\n            assert(2 * count <= outCount);\n            if constexpr (std::is_same_v<T, std::uint32_t> && std::is_same_v<TTarget, std::uint32_t>) {\n                decodeMortonCodes<T, TTarget, true>(\n                    values, count, out, outCount, mortonMetadata.getNumBits(), mortonMetadata.getCoordinateShift());\n            } else {\n                throw std::runtime_error(\"Logical level technique MORTON not implemented for 64-bit values\");\n            }\n            break;\n        }\n        case LogicalLevelTechnique::PSEUDODECIMAL:\n        default:\n            throw std::runtime_error(\"The specified logical level technique is not supported for integers: \" +\n                                     std::to_string(std::to_underlying(streamMetadata.getLogicalLevelTechnique1())));\n            break;\n    }\n}\n\ntemplate <typename TDecode, typename TInt, typename TTarget>\nvoid IntegerDecoder::decodeIntStream(BufferStream& tileData,\n                                     std::vector<TTarget>& out,\n                                     const StreamMetadata& metadata,\n                                     const bool isSigned) {\n    auto tempBuffer = getTempBuffer<TInt>(metadata.getNumValues());\n    decodeIntStream<TDecode, TInt, TTarget>(tileData, tempBuffer, metadata.getNumValues(), out, metadata, isSigned);\n}\n\ntemplate <typename TDecode, typename TInt, typename TTarget>\nvoid IntegerDecoder::decodeIntStream(BufferStream& tileData,\n                                     TInt* buffer,\n                                     std::size_t bufferSize,\n                                     std::vector<TTarget>& out,\n                                     const StreamMetadata& metadata,\n                                     const bool isSigned) {\n    decodeStream<TDecode, TInt>(tileData, buffer, bufferSize, metadata);\n    out.resize(getIntArrayBufferSize(bufferSize, metadata));\n    decodeIntArray<TInt, TTarget>(buffer, bufferSize, out.data(), out.size(), metadata, isSigned);\n}\n\ntemplate <typename TDecode, typename TInt, typename TTarget>\nTTarget IntegerDecoder::decodeConstIntStream(BufferStream& tileData,\n                                             const StreamMetadata& metadata,\n                                             const bool isSigned) {\n    TInt buffer[2] = {0};\n    decodeStream<TDecode, TInt>(tileData, &buffer[0], countof(buffer), metadata);\n\n    if (metadata.getNumValues() < 1 || metadata.getNumValues() > 2) {\n        throw std::runtime_error(\"unexpected number of values in constant stream\");\n    }\n\n    if (metadata.getNumValues() == 1) {\n        return isSigned ? decodeZigZagValue(buffer[0]) : buffer[0];\n    }\n    return isSigned ? decodeZigZagValue(buffer[1]) : buffer[1];\n}\n\ntemplate <typename TDecode, typename TInt, typename TTarget, bool Delta>\nvoid IntegerDecoder::decodeMortonStream(BufferStream& tileData,\n                                        std::vector<TTarget>& out,\n                                        const MortonEncodedStreamMetadata& metadata) {\n    auto tempBuffer = getTempBuffer<TInt>(metadata.getNumValues());\n    out.resize(2 * metadata.getNumValues());\n    decodeMortonStream<TDecode, TInt, TTarget, Delta>(\n        tileData, tempBuffer, metadata.getNumValues(), out.data(), out.size(), metadata);\n}\n\ntemplate <typename TDecode, typename TInt, typename TTarget, bool Delta>\nvoid IntegerDecoder::decodeMortonStream(BufferStream& tileData,\n                                        TInt* buffer,\n                                        std::size_t bufferCount,\n                                        TTarget* out,\n                                        std::size_t outCount,\n                                        const MortonEncodedStreamMetadata& metadata) {\n    decodeStream<TDecode, TInt>(tileData, buffer, bufferCount, metadata);\n    assert(outCount == 2 * bufferCount);\n    decodeMortonCodes<TInt, TTarget, Delta>(\n        buffer, bufferCount, out, outCount, metadata.getNumBits(), metadata.getCoordinateShift());\n}\n\n#pragma endregion\n\n#pragma region Private\n\ntemplate <typename TDecode, typename TTarget>\n    requires(std::is_integral_v<TDecode> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>))\nvoid IntegerDecoder::decodeStream(BufferStream& tileData,\n                                  TTarget* out,\n                                  std::size_t outSize,\n                                  const StreamMetadata& metadata) {\n    using namespace metadata::stream;\n\n    assert(outSize >= metadata.getNumValues());\n\n    switch (metadata.getPhysicalLevelTechnique()) {\n        case PhysicalLevelTechnique::FAST_PFOR: {\n            std::uint32_t* outPtr = nullptr;\n            std::optional<BufWrapper<std::uint32_t>> tempBuffer;\n            if constexpr (sizeof(*out) == sizeof(std::uint32_t)) {\n                // Decode directly into the output buffer\n                outPtr = reinterpret_cast<std::uint32_t*>(out);\n            } else {\n                // Decode into a 32-bit temprary buffer ...\n                tempBuffer.emplace(getTempBuffer<std::uint32_t>(metadata.getNumValues()));\n                outPtr = tempBuffer->get();\n            }\n\n            const auto resultLength = decodeFastPfor(\n                tileData, outPtr, metadata.getNumValues(), metadata.getByteLength());\n\n            if constexpr (sizeof(*out) != sizeof(std::uint32_t)) {\n                // ... then extend to 64-bit output (`.mapToLong(i -> i)` in Java)\n                std::transform(outPtr, outPtr + resultLength, out, [](auto x) { return static_cast<TTarget>(x); });\n            }\n\n            break;\n        }\n        case PhysicalLevelTechnique::VARINT:\n            util::decoding::decodeVarints<TDecode>(tileData, static_cast<std::uint32_t>(outSize), out);\n            break;\n        default:\n            throw std::runtime_error(\"Specified physical level technique not yet supported \" +\n                                     std::to_string(std::to_underlying(metadata.getPhysicalLevelTechnique())));\n    }\n}\n\ntemplate <typename T>\nvoid IntegerDecoder::decodeRLE(const std::vector<T>& values, std::vector<T>& out, const std::uint32_t numRuns) {\n    std::uint32_t outPos = 0;\n    for (std::uint32_t i = 0; i < numRuns; ++i) {\n        const auto run = values[i];\n        const auto value = values[i + numRuns];\n        if (outPos + run > out.size()) {\n            throw std::runtime_error(\"RLE run exceeds output buffer size\");\n        }\n        std::fill(std::next(out.begin(), outPos), std::next(out.begin(), outPos + run), value);\n    }\n}\n\ntemplate <typename T>\nvoid IntegerDecoder::decodeDeltaRLE(const std::vector<T>& values, std::vector<T>& out, const std::uint32_t numRuns) {\n    std::uint32_t outPos = 0;\n    T previousValue = 0;\n    for (std::uint32_t i = 0; i < numRuns; ++i) {\n        const auto run = values[i];\n        if (outPos + run > out.size()) {\n            throw std::runtime_error(\"RLE run exceeds output buffer size\");\n        }\n\n        const auto value = static_cast<std::make_signed_t<T>>(values[i + numRuns]);\n        const auto delta = util::decoding::decodeZigZag(value);\n        for (std::size_t j = 0; j < run; ++j) {\n            out[outPos++] = static_cast<T>(previousValue += delta);\n        }\n    }\n}\n\ntemplate <typename T, typename TTarget>\n    requires(std::is_integral_v<underlying_type_t<T>> && std::is_integral_v<underlying_type_t<TTarget>> &&\n             sizeof(T) <= sizeof(TTarget))\nvoid IntegerDecoder::decodeZigZagDelta(const T* values,\n                                       const std::size_t count,\n                                       TTarget* const out,\n                                       const std::size_t outCount) noexcept {\n    using namespace util::decoding;\n    assert(count == outCount);\n    std::uint32_t pos = 0;\n    using ST = std::make_signed_t<underlying_type_t<T>>;\n    ST previousValue = 0;\n    for (const auto zigZagDelta : std::span{values, count}) {\n        const auto delta = static_cast<ST>(decodeZigZag(zigZagDelta));\n        out[pos++] = static_cast<TTarget>(previousValue += delta);\n    }\n}\n\ntemplate <typename TDecode, typename TTarget, bool delta>\n    requires(std::is_integral_v<TDecode> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>))\nvoid IntegerDecoder::decodeMortonCodes(const TDecode* const data,\n                                       const std::size_t count,\n                                       TTarget* const out,\n                                       std::size_t outCount,\n                                       int numBits,\n                                       int coordinateShift) noexcept {\n    using namespace util::decoding;\n    assert(outCount == 2 * count);\n    std::uint32_t previousMortonCode = 0;\n    for (std::size_t i = 0, j = 0; i < count; ++i) {\n        auto mortonCode = static_cast<std::uint32_t>(data[i]);\n        if constexpr (delta) {\n            mortonCode += previousMortonCode;\n        };\n        out[j++] = static_cast<TTarget>(util::MortonCurve::decode(mortonCode, numBits) - coordinateShift);\n        out[j++] = static_cast<TTarget>(util::MortonCurve::decode(mortonCode >> 1, numBits) - coordinateShift);\n        if constexpr (delta) {\n            previousMortonCode = mortonCode;\n        }\n    }\n}\n\n#pragma endregion Private\n\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decode/property.hpp",
    "content": "#pragma once\n\n#include <algorithm>\n#include <iterator>\n#include <mlt/decode/int.hpp>\n#include <mlt/decode/string.hpp>\n#include <mlt/feature.hpp>\n#include <mlt/metadata/stream.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/properties.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/packed_bitset.hpp>\n#include <mlt/util/rle.hpp>\n#include <mlt/util/raw.hpp>\n\n#include <stdexcept>\n#include <string>\n#include <variant>\n#include <vector>\n\nnamespace mlt::decoder {\n\nclass PropertyDecoder {\npublic:\n    PropertyDecoder(IntegerDecoder& intDecoder_, StringDecoder& stringDecoder_)\n        : intDecoder(intDecoder_),\n          stringDecoder(stringDecoder_) {}\n\n    PropertyVecMap decodePropertyColumn(BufferStream& tileData,\n                                        const metadata::tileset::Column& column,\n                                        std::uint32_t numStreams) {\n        using namespace metadata::tileset;\n        if (std::holds_alternative<ScalarColumn>(column.type)) {\n            PropertyVecMap results;\n            results.emplace(column.name, decodeScalarPropertyColumn(tileData, column, numStreams));\n            return results;\n        }\n\n        if (numStreams > 1) {\n            return stringDecoder.decodeSharedDictionary(tileData, column, numStreams);\n        }\n\n        skipColumn(tileData, numStreams);\n        return {};\n    }\n\nprotected:\n    void skipColumn(BufferStream& tileData, std::uint32_t numStreams) {\n        using namespace metadata::stream;\n        using namespace util::decoding;\n\n        for (std::uint32_t i = 0; i < numStreams; ++i) {\n            auto streamMetadata = StreamMetadata::decode(tileData);\n            if (!streamMetadata) {\n                throw std::runtime_error(\"Failed to decode stream metadata\");\n            }\n\n            // Skip the stream data\n            tileData.consume(streamMetadata->getByteLength());\n        }\n    }\n\n    PresentProperties decodeScalarPropertyColumn(BufferStream& tileData,\n                                                 const metadata::tileset::Column& column,\n                                                 std::uint32_t numStreams) {\n        using namespace metadata;\n        using namespace metadata::stream;\n        using namespace metadata::tileset;\n        using namespace util::decoding;\n\n        PackedBitset presentStream;\n        std::uint32_t presentValueCount = 0;\n\n        if (column.nullable) {\n            const auto presentStreamMetadata = StreamMetadata::decode(tileData);\n            presentValueCount = presentStreamMetadata->getNumValues();\n            rle::decodeBoolean(tileData, presentStream, *presentStreamMetadata, /*consume=*/true);\n            if ((presentValueCount + 7) / 8 != presentStream.size()) {\n                throw std::runtime_error(\"invalid present stream\");\n            }\n            numStreams -= 1;\n        }\n\n        const auto scalarColumn = std::get<ScalarColumn>(column.type);\n        if (!scalarColumn.hasPhysicalType()) {\n            throw std::runtime_error(\"property column ('\" + column.name + \"') must be scalar\");\n        }\n        const auto scalarType = scalarColumn.getPhysicalType();\n\n        // String stream metadata is read by StringDecoder\n        std::unique_ptr<StreamMetadata> streamMetadata;\n        if (scalarType != ScalarType::STRING) {\n            streamMetadata = StreamMetadata::decode(tileData);\n        }\n\n        if (column.nullable && streamMetadata && presentValueCount < streamMetadata->getNumValues()) {\n            throw std::runtime_error(\"Unexpected present value column\");\n        }\n\n        const auto checkBits = [&](const auto& presentBuffer, const auto& propertyBuffer, bool isBoolean = false) {\n#ifndef NDEBUG\n            if (!presentStream.empty()) {\n                const auto actualProperties = propertyCount(propertyBuffer, isBoolean);\n                const auto presentBits = countSetBits(presentBuffer);\n                if ((isBoolean && actualProperties / 8 != (presentBits + 7) / 8) ||\n                    (!isBoolean && actualProperties != presentBits)) {\n                    throw std::runtime_error(\"Property count \" + std::to_string(actualProperties) +\n                                             \" doesn't match present bits \" + std::to_string(presentBits));\n                }\n            }\n#endif\n        };\n\n        switch (scalarType) {\n            case ScalarType::BOOLEAN: {\n                std::vector<std::uint8_t> byteBuffer;\n                rle::decodeBoolean(tileData, byteBuffer, *streamMetadata, /*consume=*/true);\n                if (streamMetadata->getNumValues() > 0 &&\n                    (streamMetadata->getNumValues() + 7) / 8 != byteBuffer.size()) {\n                    throw std::runtime_error(\"column data incomplete\");\n                }\n\n                checkBits(presentStream, byteBuffer, /*isBoolean=*/true);\n                return {scalarType, byteBuffer, presentStream};\n            }\n            case ScalarType::INT_8:\n            case ScalarType::UINT_8:\n                throw std::runtime_error(\"8-bit integer type not implemented\");\n            case ScalarType::INT_32:\n            case ScalarType::UINT_32: {\n                const bool isSigned = (scalarType == ScalarType::INT_32);\n                PropertyVec result;\n                if (isSigned) {\n                    std::vector<std::int32_t> intBuffer;\n                    intBuffer.reserve(streamMetadata->getNumValues());\n                    intDecoder.decodeIntStream<std::uint32_t, std::uint32_t, std::int32_t>(\n                        tileData, intBuffer, *streamMetadata, isSigned);\n                    result = {std::move(intBuffer)};\n                } else {\n                    std::vector<std::uint32_t> uintBuffer;\n                    uintBuffer.reserve(streamMetadata->getNumValues());\n                    intDecoder.decodeIntStream<std::uint32_t, std::uint32_t, std::uint32_t>(\n                        tileData, uintBuffer, *streamMetadata, isSigned);\n                    result = {std::move(uintBuffer)};\n                }\n\n                checkBits(presentStream, result);\n                return {scalarType, std::move(result), std::move(presentStream)};\n            }\n            case ScalarType::INT_64: {\n                std::vector<std::int64_t> longBuffer;\n                longBuffer.reserve(streamMetadata->getNumValues());\n                intDecoder.decodeIntStream<std::uint64_t, std::uint64_t, std::int64_t>(\n                    tileData, longBuffer, *streamMetadata, /*isSigned=*/true);\n\n                PropertyVec result{std::move(longBuffer)};\n                checkBits(presentStream, result);\n                return {scalarType, std::move(result), std::move(presentStream)};\n            }\n            case ScalarType::UINT_64: {\n                std::vector<std::uint64_t> longBuffer;\n                longBuffer.reserve(streamMetadata->getNumValues());\n                intDecoder.decodeIntStream<std::uint64_t, std::uint64_t, std::uint64_t>(\n                    tileData, longBuffer, *streamMetadata, /*isSigned=*/false);\n\n                PropertyVec result{std::move(longBuffer)};\n                checkBits(presentStream, result);\n                return {scalarType, std::move(result), std::move(presentStream)};\n            }\n            case ScalarType::DOUBLE: {\n                std::vector<double> doubleBuffer;\n                if (streamMetadata->getNumValues() * sizeof(double) == streamMetadata->getByteLength()) {\n                    decodeRaw(tileData, doubleBuffer, *streamMetadata, /*consume=*/true);\n                } else {\n                    // Compatibility with tilesets encoded before double support was added\n                    std::vector<float> floatBuffer;\n                    decodeRaw(tileData, floatBuffer, *streamMetadata, /*consume=*/true);\n                    doubleBuffer.reserve(streamMetadata->getNumValues());\n                    std::ranges::transform(floatBuffer, std::back_inserter(doubleBuffer), [](float value) {\n                        return static_cast<double>(value);\n                    });\n                }\n\n                PropertyVec result{std::move(doubleBuffer)};\n                checkBits(presentStream, result);\n                return {scalarType, std::move(result), std::move(presentStream)};\n            }\n            case ScalarType::FLOAT: {\n                std::vector<float> floatBuffer;\n                decodeRaw(tileData, floatBuffer, *streamMetadata, /*consume=*/true);\n\n                PropertyVec result{std::move(floatBuffer)};\n                checkBits(presentStream, result);\n                return {scalarType, std::move(result), std::move(presentStream)};\n            }\n            case ScalarType::STRING: {\n                auto strings = stringDecoder.decode(tileData, numStreams);\n                if (column.nullable && countSetBits(presentStream) != strings.getStrings().size()) {\n                    throw std::runtime_error(\"String count doesn't match present value count\");\n                }\n\n                PropertyVec result{std::move(strings)};\n                checkBits(presentStream, result);\n                return {scalarType, PropertyVec{std::move(result)}, std::move(presentStream)};\n            }\n            default:\n                throw std::runtime_error(\"Unknown scalar type: \" + std::to_string(std::to_underlying(scalarType)));\n        }\n    }\n\nprivate:\n    IntegerDecoder& intDecoder;\n    StringDecoder& stringDecoder;\n};\n\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decode/string.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/decode/int.hpp>\n#include <mlt/metadata/stream.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/properties.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/packed_bitset.hpp>\n#include <mlt/util/raw.hpp>\n#include <mlt/util/varint.hpp>\n\n#include <string>\n#include <stdexcept>\n#include <string_view>\n\nnamespace mlt::decoder {\n\nclass StringDecoder {\npublic:\n    StringDecoder(IntegerDecoder& intDecoder_)\n        : intDecoder(intDecoder_) {}\n\n    /*\n     * String column layouts:\n     * -> plain -> present, length, data\n     * -> dictionary -> present, length, dictionary, data\n     * -> fsst dictionary -> symbolTable, symbolLength, dictionary, length, present, data\n     * */\n\n    StringDictViews decode(BufferStream& tileData, std::uint32_t numStreams) {\n        using namespace metadata::stream;\n        using namespace util::decoding;\n\n        std::optional<DictionaryType> dictType;\n        std::optional<LengthType> lengthType;\n        std::vector<std::uint8_t> dictionaryStream;\n        std::vector<std::uint8_t> symbolStream;\n        std::vector<std::uint32_t> offsetStream;\n        std::vector<std::uint32_t> dictLengthStream;\n        std::vector<std::uint32_t> symbolLengthStream;\n        std::vector<std::string_view> views;\n        for (std::uint32_t i = 0; i < numStreams; ++i) {\n            const auto streamMetadata = StreamMetadata::decode(tileData);\n            switch (streamMetadata->getPhysicalStreamType()) {\n                case PhysicalStreamType::OFFSET:\n                    intDecoder.decodeIntStream<std::uint32_t>(tileData, offsetStream, *streamMetadata);\n                    break;\n                case PhysicalStreamType::LENGTH: {\n                    if (!streamMetadata->getLogicalStreamType() ||\n                        !streamMetadata->getLogicalStreamType()->getLengthType()) {\n                        throw std::runtime_error(\"Length stream missing logical type\");\n                    }\n                    lengthType = streamMetadata->getLogicalStreamType()->getLengthType();\n                    auto& target = (lengthType == LengthType::DICTIONARY) ? dictLengthStream : symbolLengthStream;\n                    intDecoder.decodeIntStream<std::uint32_t>(tileData, target, *streamMetadata);\n                    break;\n                }\n                case PhysicalStreamType::DATA: {\n                    if (!streamMetadata->getLogicalStreamType() ||\n                        !streamMetadata->getLogicalStreamType()->getDictionaryType()) {\n                        throw std::runtime_error(\"Data stream missing logical type\");\n                    }\n                    dictType = streamMetadata->getLogicalStreamType()->getDictionaryType();\n                    auto& target = (dictType == DictionaryType::SINGLE) ? dictionaryStream : symbolStream;\n                    decodeRaw(tileData, target, streamMetadata->getByteLength(), /*consume=*/true);\n                    break;\n                }\n                default:\n                    throw std::runtime_error(\"Unsupported stream type\");\n            }\n        }\n\n        if (!dictLengthStream.empty() && !symbolLengthStream.empty()) {\n            auto data = decodeFSST(symbolStream, symbolLengthStream, dictionaryStream, 2 * dictionaryStream.size());\n            decodeDictionary(dictLengthStream, data, offsetStream, views);\n            return {std::move(data), std::move(views)};\n        } else if (!offsetStream.empty() && !dictLengthStream.empty()) {\n            decodeDictionary(dictLengthStream, dictionaryStream, offsetStream, views);\n            return {std::move(dictionaryStream), std::move(views)};\n        } else if (!symbolLengthStream.empty()) {\n            decodePlain(symbolLengthStream, symbolStream, views);\n            return {std::move(symbolStream), std::move(views)};\n        } else {\n            throw std::runtime_error(\"Expected streams missing in string decoding\");\n        }\n    }\n\n    /// Multiple string columns sharing a dictionary\n    PropertyVecMap decodeSharedDictionary(BufferStream& tileData,\n                                          const metadata::tileset::Column& column,\n                                          std::uint32_t numStreams) {\n        using namespace metadata::stream;\n        using namespace metadata::tileset::schema;\n        using namespace util::decoding;\n\n        if (!column.hasComplexType() || !column.getComplexType().hasChildren() || numStreams < 3) {\n            throw std::runtime_error(\"Expected struct column for shared dictionary decoding\");\n        }\n\n        std::vector<std::uint32_t> dictionaryLengthStream;\n        auto dictionaryStream = std::make_shared<std::vector<std::uint8_t>>();\n        std::vector<std::uint32_t> symbolLengthStream;\n        std::vector<std::uint8_t> symbolTableStream;\n        PropertyVecMap results;\n\n        bool dictionaryStreamDecoded = false;\n        while (!dictionaryStreamDecoded && numStreams--) {\n            const auto streamMetadata = metadata::stream::StreamMetadata::decode(tileData);\n            switch (streamMetadata->getPhysicalStreamType()) {\n                case PhysicalStreamType::LENGTH: {\n                    const bool isDict = streamMetadata->getLogicalStreamType()->getLengthType() ==\n                                        LengthType::DICTIONARY;\n                    auto& target = isDict ? dictionaryLengthStream : symbolLengthStream;\n                    intDecoder.decodeIntStream<std::uint32_t>(tileData, target, *streamMetadata);\n                    break;\n                }\n                case PhysicalStreamType::DATA: {\n                    const auto type = streamMetadata->getLogicalStreamType()->getDictionaryType();\n                    const auto isDict = (type == DictionaryType::SINGLE || type == DictionaryType::SHARED);\n                    auto& targetStream = isDict ? *dictionaryStream : symbolTableStream;\n                    decodeRaw(tileData, targetStream, streamMetadata->getByteLength(), /*consume=*/true);\n                    dictionaryStreamDecoded = isDict;\n                    break;\n                }\n                default:\n                    throw std::runtime_error(\"Unsupported stream type\");\n            }\n        }\n\n        std::vector<std::string_view> dictionaryViews;\n        if (!symbolLengthStream.empty() && !symbolTableStream.empty() && !dictionaryLengthStream.empty()) {\n            auto data = std::make_shared<std::vector<std::uint8_t>>(\n                decodeFSST(symbolTableStream, symbolLengthStream, *dictionaryStream, 2 * dictionaryStream->size()));\n            decodeDictionary(dictionaryLengthStream, *data, dictionaryViews);\n            // retain the decoded dictionary data instead of the original raw data\n            dictionaryStream.swap(data);\n        } else if (!dictionaryLengthStream.empty() && !dictionaryStream->empty()) {\n            decodeDictionary(dictionaryLengthStream, *dictionaryStream, dictionaryViews);\n        } else {\n            throw std::runtime_error(\"Expected streams missing in shared dictionary decoding\");\n        }\n\n        for (const auto& child : column.getComplexType().children) {\n            [[maybe_unused]] const auto childStreams = decodeVarint<std::uint32_t>(tileData);\n\n            if (!child.hasScalarType() || child.getScalarType().getPhysicalType() != ScalarType::STRING) {\n                throw std::runtime_error(\"Currently only string fields are implemented for a struct\");\n            }\n\n            PackedBitset presentStream;\n            if (child.nullable) {\n                const auto presentStreamMetadata = metadata::stream::StreamMetadata::decode(tileData);\n                const auto presentValueCount = presentStreamMetadata->getNumValues();\n                rle::decodeBoolean(tileData, presentStream, *presentStreamMetadata, /*consume=*/true);\n                if ((presentValueCount + 7) / 8 != presentStream.size()) {\n                    throw std::runtime_error(\"invalid present stream\");\n                }\n            }\n\n            const auto dataStreamMetadata = metadata::stream::StreamMetadata::decode(tileData);\n            std::vector<std::uint32_t> dataReferenceStream;\n            intDecoder.decodeIntStream<std::uint32_t>(tileData, dataReferenceStream, *dataStreamMetadata);\n\n            std::vector<std::string_view> propertyValues;\n            propertyValues.reserve(dataReferenceStream.size());\n\n            for (std::uint32_t i = 0; i < dataReferenceStream.size(); ++i) {\n                const auto dictIndex = dataReferenceStream[i];\n                if (dictIndex >= dictionaryViews.size()) {\n                    throw std::runtime_error(\"StringDecoder: dictionaryViews index out of bounds\");\n                }\n                propertyValues.push_back(dictionaryViews[dictIndex]);\n            }\n\n            results.emplace(column.name + child.name,\n                            PresentProperties{child.getScalarType().getPhysicalType(),\n                                              StringDictViews(dictionaryStream, propertyValues),\n                                              std::move(presentStream)});\n        }\n\n        return results;\n    }\n\n    static std::vector<std::uint8_t> decodeFSST(const std::vector<std::uint8_t>& symbols,\n                                                const std::vector<std::uint32_t>& symbolLengths,\n                                                const std::vector<std::uint8_t>& compressedData,\n                                                std::size_t decompressedLength) {\n        return decodeFSST(symbols.data(),\n                          symbols.size(),\n                          symbolLengths.data(),\n                          symbolLengths.size(),\n                          compressedData.data(),\n                          compressedData.size(),\n                          decompressedLength);\n    }\n\n    static std::vector<std::uint8_t> decodeFSST(const std::uint8_t* symbols,\n                                                const std::size_t symbolCount,\n                                                const std::uint32_t* symbolLengths,\n                                                const std::size_t symbolLengthCount,\n                                                const std::uint8_t* compressedData,\n                                                const std::size_t compressedDataCount,\n                                                const std::size_t decompressedLength) {\n        std::vector<std::uint8_t> output;\n\n        if (decompressedLength > 0) {\n            output.resize(decompressedLength);\n        }\n        std::vector<std::uint32_t> symbolOffsets(symbolLengthCount);\n        for (size_t i = 1; i < symbolLengthCount; i++) {\n            symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];\n        }\n\n        std::size_t idx = 0;\n        for (size_t i = 0; i < compressedDataCount; i++) {\n            const std::uint8_t symbolIndex = compressedData[i];\n\n            // 255 is our escape byte -> take the next symbol as it is\n            if (symbolIndex == 255) {\n                if (idx == output.size()) {\n                    output.resize(output.size() * 2);\n                }\n                output[idx++] = compressedData[++i];\n            } else if (symbolIndex < symbolLengthCount) {\n                const auto len = symbolLengths[symbolIndex];\n                if (idx + len > output.size()) {\n                    output.resize((output.size() + len) * 2);\n                }\n                const auto offset = symbolOffsets[symbolIndex];\n                if (offset >= symbolCount) {\n                    throw std::runtime_error(\"FSST decode: symbol index out of bounds\");\n                }\n                std::memcpy(&output[idx], &symbols[offset], len);\n                idx += len;\n            } else {\n                throw std::runtime_error(\"FSST decode: invalid symbol index\");\n            }\n        }\n\n        output.resize(idx);\n        return output;\n    }\n\nprivate:\n    IntegerDecoder& intDecoder;\n\n    /// Drop the useless codepoint produced when a UTF-16 Byte-Order-Mark is included in the conversion to UTF-8\n    // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8\n    // TODO: Can we force this on the encoding side instead?\n    static std::string_view view(const char* bytes, std::size_t length) {\n        if (length >= 3 && std::equal(bytes, bytes + 3, \"\\xEF\\xBB\\xBF\")) {\n            bytes += 3;\n            length -= 3;\n        }\n        return {bytes, length};\n    }\n\n    static void decodePlain(const std::vector<std::uint32_t>& lengthStream,\n                            const std::vector<std::uint8_t>& utf8bytes,\n                            std::vector<std::string_view>& out) {\n        std::size_t dataOffset = 0;\n        std::size_t lengthOffset = 0;\n        out.reserve(lengthStream.size());\n        for (std::uint32_t i = 0; i < lengthStream.size(); ++i) {\n            const auto length = lengthStream[lengthOffset++];\n            const char* bytes = reinterpret_cast<std::string::const_pointer>(utf8bytes.data() + dataOffset);\n            out.push_back(view(bytes, length));\n            dataOffset += length;\n        }\n    }\n\n    static void decodeDictionary(const std::vector<std::uint32_t>& lengthStream,\n                                 const std::vector<std::uint8_t>& utf8bytes,\n                                 std::vector<std::string_view>& out) {\n        const auto* const utf8Ptr = reinterpret_cast<const char*>(utf8bytes.data());\n\n        std::size_t offset = 0;\n        for (auto length : lengthStream) {\n            out.emplace_back(utf8Ptr + offset, length);\n            offset += length;\n        }\n    }\n\n    static void decodeDictionary(const std::vector<std::uint32_t>& lengthStream,\n                                 const std::vector<std::uint8_t>& utf8bytes,\n                                 const std::vector<std::uint32_t>& offsets,\n                                 std::vector<std::string_view>& out) {\n        const auto* const utf8Ptr = reinterpret_cast<const char*>(utf8bytes.data());\n\n        std::vector<std::string_view> dictionary;\n        dictionary.reserve(lengthStream.size());\n\n        std::uint32_t dictionaryOffset = 0;\n        for (const auto length : lengthStream) {\n            dictionary.push_back(view(utf8Ptr + dictionaryOffset, length));\n            dictionaryOffset += length;\n        }\n\n        out.reserve(offsets.size());\n        for (std::uint32_t i = 0; i < offsets.size(); ++i) {\n            out.push_back(dictionary[offsets[i]]);\n        }\n    }\n};\n\n} // namespace mlt::decoder\n"
  },
  {
    "path": "cpp/src/mlt/decoder.cpp",
    "content": "#include <mlt/decoder.hpp>\n\n#include <mlt/decode/geometry.hpp>\n#include <mlt/decode/int.hpp>\n#include <mlt/decode/int_template.hpp>\n#include <mlt/decode/property.hpp>\n#include <mlt/decode/string.hpp>\n#include <mlt/geometry.hpp>\n#include <mlt/geometry_vector.hpp>\n#include <mlt/layer.hpp>\n#include <mlt/metadata/type_map.hpp>\n#include <mlt/metadata/stream.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/properties.hpp>\n#include <mlt/tile.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/packed_bitset.hpp>\n#include <mlt/util/rle.hpp>\n#include <mlt/util/stl.hpp>\n#include <mlt/util/varint.hpp>\n\n#include <cstddef>\n#include <optional>\n#include <stdexcept>\n\nnamespace mlt {\n\nusing namespace decoder;\nusing namespace util::decoding;\n\nstruct Decoder::Impl {\n    Impl(std::unique_ptr<GeometryFactory>&& geometryFactory_, bool supportFastPFOR)\n        : integerDecoder(supportFastPFOR),\n          geometryDecoder(integerDecoder),\n          geometryFactory(std::move(geometryFactory_)) {}\n\n    Layer parseBasicMVTEquivalent(BufferStream&);\n    std::vector<Feature> makeFeatures(const std::vector<std::optional<Feature::id_t>>&,\n                                      std::vector<std::unique_ptr<Geometry>>&&);\n\n    IntegerDecoder integerDecoder;\n    StringDecoder stringDecoder{integerDecoder};\n    PropertyDecoder propertyDecoder{integerDecoder, stringDecoder};\n    GeometryDecoder geometryDecoder;\n    std::unique_ptr<GeometryFactory> geometryFactory;\n};\n\nLayer Decoder::Impl::parseBasicMVTEquivalent(BufferStream& tileData) {\n    using metadata::stream::StreamMetadata;\n    using metadata::type_map::Tag0x01;\n\n    const auto layerMetadata = mlt::metadata::tileset::decodeFeatureTable(tileData);\n\n    std::vector<std::optional<Feature::id_t>> ids;\n    std::unique_ptr<geometry::GeometryVector> geometryVector;\n    PropertyVecMap properties;\n\n    for (const auto& columnMetadata : layerMetadata.columns) {\n        const auto numStreams = Tag0x01::hasStreamCount(columnMetadata) ? decodeVarint<std::uint32_t>(tileData) : 1;\n        if (columnMetadata.isID()) {\n            PackedBitset idPresentBits;\n            std::uint32_t numFeaturesFromPresent = 0;\n            if (columnMetadata.nullable) {\n                const auto presentStreamMetadata = StreamMetadata::decode(tileData);\n                numFeaturesFromPresent = presentStreamMetadata->getNumValues();\n                rle::decodeBoolean(tileData, idPresentBits, *presentStreamMetadata, /*consume=*/true);\n            }\n\n            const auto idDataStreamMetadata = StreamMetadata::decode(tileData);\n\n            std::vector<Feature::id_t> denseIds(idDataStreamMetadata->getNumValues());\n            if (columnMetadata.getScalarType().hasLongID) {\n                integerDecoder.decodeIntStream<std::uint64_t>(tileData, denseIds, *idDataStreamMetadata);\n            } else {\n                integerDecoder.decodeIntStream<std::uint32_t, std::uint64_t>(tileData, denseIds, *idDataStreamMetadata);\n            }\n\n            if (!idPresentBits.empty()) {\n                // Expand dense IDs to sparse optional IDs using the present bitset\n                ids.resize(numFeaturesFromPresent);\n                std::size_t denseIdx = 0;\n                for (std::uint32_t i = 0; i < numFeaturesFromPresent; ++i) {\n                    if (testBit(idPresentBits, i)) {\n                        ids[i] = denseIds[denseIdx++];\n                    }\n                }\n            } else {\n                ids.resize(denseIds.size());\n                for (std::size_t i = 0; i < denseIds.size(); ++i) {\n                    ids[i] = denseIds[i];\n                }\n            }\n        } else if (columnMetadata.isGeometry()) {\n            geometryVector = geometryDecoder.decodeGeometryColumn(tileData, columnMetadata, numStreams);\n        } else {\n            // Decode the property column, which may result in multiple properties (e.g. struct), and merge the results\n            auto columnProperties = propertyDecoder.decodePropertyColumn(tileData, columnMetadata, numStreams);\n            std::copy(std::make_move_iterator(columnProperties.begin()),\n                      std::make_move_iterator(columnProperties.end()),\n                      std::inserter(properties, properties.end()));\n        }\n    }\n\n    // Check framing, we expect to use all the data in the buffer provided.\n    if (tileData.available() > 0) {\n        throw std::runtime_error(std::to_string(tileData.available()) + \" bytes trailing layer \" + layerMetadata.name);\n    }\n\n    return {layerMetadata.name,\n            layerMetadata.extent,\n            std::move(geometryVector),\n            makeFeatures(ids, geometryVector->getGeometries(*geometryFactory)),\n            std::move(properties)};\n}\n\nstd::vector<Feature> Decoder::Impl::makeFeatures(const std::vector<std::optional<Feature::id_t>>& ids,\n                                                 std::vector<std::unique_ptr<Geometry>>&& geometries) {\n    const auto featureCount = geometries.size();\n    if (!ids.empty() && ids.size() != featureCount) {\n        throw std::runtime_error(\"ID count (\" + std::to_string(ids.size()) + \") does not match geometry count (\" +\n                                 std::to_string(featureCount) + \")\");\n    }\n\n    return util::generateVector<Feature>(featureCount, [&](const auto i) {\n        const auto id = ids.empty() ? std::nullopt : ids[i];\n        return Feature{id, std::move(geometries[i]), static_cast<std::uint32_t>(i)};\n    });\n}\n\nDecoder::Decoder(bool supportFastPFOR)\n    : Decoder(std::make_unique<GeometryFactory>(), supportFastPFOR) {}\n\nDecoder::Decoder(std::unique_ptr<GeometryFactory>&& geometryFactory, bool supportFastPFOR)\n    : impl{std::make_unique<Impl>(std::move(geometryFactory), supportFastPFOR)} {}\n\nDecoder::~Decoder() noexcept = default;\n\nMapLibreTile Decoder::decode(BufferStream tileData) {\n    std::vector<Layer> layers;\n    while (tileData.available()) {\n        const auto layerLength = decodeVarint<std::uint32_t>(tileData);\n\n        // Create a new BufferStream for this layer and advance past it in the main stream\n        auto layerStream = tileData.getSubStream(0, layerLength);\n        tileData.consume(layerLength);\n\n        const auto layerTag = decodeVarint<std::uint32_t>(layerStream);\n        if (layerTag == 1) {\n            layers.push_back(impl->parseBasicMVTEquivalent(layerStream));\n        } else {\n            // Skipping unknown layer\n        }\n    }\n    return {std::move(layers)};\n}\n\nMapLibreTile Decoder::decode(DataView tileData) {\n    return decode(BufferStream{tileData});\n}\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/src/mlt/feature.cpp",
    "content": "#include <mlt/feature.hpp>\n\n#include <mlt/geometry.hpp>\n#include <mlt/layer.hpp>\n\nnamespace mlt {\n\nFeature::Feature(std::optional<id_t> ident_, std::unique_ptr<Geometry>&& geometry_, std::uint32_t index_)\n    : ident(ident_),\n      index(index_),\n      geometry(std::move(geometry_)) {}\n\nFeature::~Feature() noexcept = default;\n\nstd::optional<Property> Feature::getProperty(const std::string& key, const Layer& layer) const {\n    const auto& propertyMap = layer.getProperties();\n    if (const auto hit = propertyMap.find(key); hit != propertyMap.end()) {\n        return hit->second.getProperty(index);\n    }\n    return std::nullopt;\n}\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/src/mlt/geometry_vector.cpp",
    "content": "#include <mlt/geometry_vector.hpp>\n\n#include <mlt/coordinate.hpp>\n#include <mlt/polyfill.hpp>\n#include <mlt/util/morton_curve.hpp>\n#include <mlt/util/stl.hpp>\n\nnamespace mlt::geometry {\n\nnamespace {\nconstexpr void checkBuffer(std::size_t index, std::size_t size, std::string_view name) {\n    if (index >= size) {\n        throw std::runtime_error(std::string(name) + \" underflow\");\n    }\n}\n#define CHECK_BUFFER(I, B) checkBuffer(I, B.size(), #B)\n\ninline Coordinate coord(std::int32_t x, std::int32_t y) {\n    return {static_cast<float>(x), static_cast<float>(y)};\n}\n\nstd::vector<Coordinate> getLineStringCoords(const std::vector<std::int32_t>& vertexBuffer,\n                                            std::uint32_t startIndex,\n                                            const std::uint32_t numVertices,\n                                            bool closeLineString) {\n    std::vector<Coordinate> coords;\n    coords.reserve(numVertices + 1);\n    CHECK_BUFFER(startIndex + numVertices - 1, vertexBuffer);\n    for (std::uint32_t i = 0; i < numVertices; ++i) {\n        const auto x = vertexBuffer[startIndex++];\n        coords.push_back(coord(x, vertexBuffer[startIndex++]));\n    }\n\n    if (closeLineString && !coords.empty() && coords.front() != coords.back()) {\n        coords.push_back(coords.front());\n    }\n    return coords;\n}\n\nstd::vector<Coordinate> getDictionaryEncodedLineStringCoords(const std::vector<std::int32_t>& vertexBuffer,\n                                                             const std::vector<std::uint32_t>& vertexOffsets,\n                                                             std::uint32_t vertexOffset,\n                                                             const std::uint32_t numVertices,\n                                                             const bool closeLineString) {\n    std::vector<Coordinate> coords;\n    coords.reserve(numVertices + 1);\n    CHECK_BUFFER(vertexOffset + numVertices - 1, vertexOffsets);\n    for (std::uint32_t i = 0; i < numVertices; ++i) {\n        const auto offset = 2 * vertexOffsets[vertexOffset++];\n        CHECK_BUFFER(offset + 1, vertexBuffer);\n        coords.push_back(coord(vertexBuffer[offset], vertexBuffer[offset + 1]));\n    }\n    if (closeLineString && !coords.empty() && coords.front() != coords.back()) {\n        coords.push_back(coords.front());\n    }\n    return coords;\n}\n\nstd::vector<Coordinate> getMortonEncodedLineStringCoords(const std::vector<std::int32_t>& vertexBuffer,\n                                                         const std::vector<std::uint32_t>& vertexOffsets,\n                                                         std::uint32_t vertexOffset,\n                                                         const std::uint32_t numVertices,\n                                                         const MortonSettings& mortonSettings,\n                                                         const bool closeLineString) {\n    std::vector<Coordinate> coords;\n    coords.reserve(numVertices + 1);\n    CHECK_BUFFER(vertexOffset + numVertices - 1, vertexOffsets);\n    for (std::uint32_t i = 0; i < numVertices; ++i) {\n        const auto offset = vertexOffsets[vertexOffset++];\n        CHECK_BUFFER(offset, vertexBuffer);\n        coords.push_back(\n            util::MortonCurve::decode(vertexBuffer[offset], mortonSettings.numBits, mortonSettings.coordinateShift));\n    }\n    if (closeLineString && !coords.empty() && coords.front() != coords.back()) {\n        coords.push_back(coords.front());\n    }\n    return coords;\n}\n\n} // namespace\n\nvoid GeometryVector::applyTriangles(Geometry& geom,\n                                    std::uint32_t& triangleOffset,\n                                    std::uint32_t& indexBufferOffset,\n                                    [[maybe_unused]] std::uint32_t totalVertices,\n                                    [[maybe_unused]] bool multiPolygon) const {\n    if (triangleCounts.empty()) {\n        return;\n    }\n\n    CHECK_BUFFER(triangleOffset, triangleCounts);\n    const auto numTriangles = triangleCounts[triangleOffset++];\n    if (numTriangles) {\n        CHECK_BUFFER(indexBufferOffset + (3 * numTriangles) - 1, indexBuffer);\n        assert(std::all_of(std::next(indexBuffer.cbegin(), indexBufferOffset),\n                           std::next(indexBuffer.cbegin(), indexBufferOffset + (3 * numTriangles)),\n                           [=](auto i) { return i < totalVertices; }));\n#if !defined(NDEBUG) && false\n        // Expect the tessellated indexes to reference the entire range of vertices.\n        // The Java implementation of Earcut makes this fail, so it's disabled for now.\n        const auto limits = std::ranges::minmax_element(&indexBuffer[indexBufferOffset],\n                                                        &indexBuffer[indexBufferOffset + (3 * numTriangles)]);\n        assert(*limits.min == 0 && *limits.max == totalVertices - 1);\n#endif\n        geom.setTriangles({&indexBuffer[indexBufferOffset], 3 * numTriangles});\n        indexBufferOffset += 3 * numTriangles;\n    }\n}\n\nstd::vector<std::unique_ptr<Geometry>> GeometryVector::getGeometries(const GeometryFactory& factory) const {\n    std::vector<std::unique_ptr<Geometry>> geometries;\n    geometries.reserve(numGeometries);\n\n    std::uint32_t partOffsetCounter = 1;\n    std::uint32_t ringOffsetsCounter = 1;\n    std::uint32_t geometryOffsetsCounter = 1;\n    std::uint32_t vertexBufferOffset = 0;\n    std::uint32_t vertexOffsetsOffset = 0;\n    std::uint32_t indexBufferOffset = 0;\n    std::uint32_t triangleOffset = 0;\n\n    const auto containsPolygon = containsPolygonGeometry();\n\n    for (std::size_t i = 0; i < numGeometries; ++i) {\n        using metadata::tileset::GeometryType;\n        const auto geomType = getGeometryType(i);\n\n        switch (geomType) {\n            case GeometryType::POINT:\n                if (vertexOffsets.empty()) {\n                    CHECK_BUFFER(vertexBufferOffset + 1, vertexBuffer);\n                    geometries.push_back(\n                        factory.createPoint({static_cast<float>(vertexBuffer[vertexBufferOffset]),\n                                             static_cast<float>(vertexBuffer[vertexBufferOffset + 1])}));\n                    vertexBufferOffset += 2;\n                } else if (vertexBufferType == VertexBufferType::VEC_2) {\n                    CHECK_BUFFER(vertexOffsetsOffset, vertexOffsets);\n                    const auto vertexOffset = vertexOffsets[vertexOffsetsOffset++] * 2;\n                    CHECK_BUFFER(vertexOffset + 1, vertexBuffer);\n                    geometries.push_back(factory.createPoint({static_cast<float>(vertexBuffer[vertexOffset]),\n                                                              static_cast<float>(vertexBuffer[vertexOffset + 1])}));\n                } else {\n                    assert(vertexBufferType == VertexBufferType::MORTON);\n                    CHECK_BUFFER(vertexOffsetsOffset, vertexOffsets);\n                    const auto mortonCode = vertexOffsets[vertexOffsetsOffset++];\n                    geometries.push_back(factory.createPoint(util::MortonCurve::decode(\n                        mortonCode, mortonSettings->numBits, mortonSettings->coordinateShift)));\n                }\n\n                geometryOffsetsCounter++;\n                partOffsetCounter++;\n                ringOffsetsCounter++;\n\n                break;\n            case GeometryType::MULTIPOINT: {\n                if (!topologyVector) {\n                    throw std::runtime_error(\"Multi-point geometry without topology vector\");\n                }\n                const auto& geometryOffsets = topologyVector->getGeometryOffsets();\n                [[maybe_unused]] const auto& partOffsets = topologyVector->getPartOffsets();\n                [[maybe_unused]] const auto& ringOffsets = topologyVector->getRingOffsets();\n\n                CHECK_BUFFER(geometryOffsetsCounter, geometryOffsets);\n                const auto numPoints = geometryOffsets[geometryOffsetsCounter] -\n                                       geometryOffsets[geometryOffsetsCounter - 1];\n                geometryOffsetsCounter++;\n\n                if (numPoints) {\n                    std::vector<Coordinate> vertices;\n                    vertices.reserve(numPoints);\n\n                    if (vertexOffsets.empty()) {\n                        CHECK_BUFFER(vertexBufferOffset + (2 * numPoints) - 1, vertexBuffer);\n                        for (std::uint32_t j = 0; j < numPoints; ++j) {\n                            const auto x = vertexBuffer[vertexBufferOffset++];\n                            vertices.push_back(coord(x, vertexBuffer[vertexBufferOffset++]));\n                        }\n                    } else {\n                        CHECK_BUFFER(vertexOffsetsOffset + numPoints - 1, vertexOffsets);\n                        for (std::uint32_t j = 0; j < numPoints; ++j) {\n                            const auto offset = vertexOffsets[vertexOffsetsOffset++] * 2;\n                            vertices.push_back(coord(vertexBuffer[offset], vertexBuffer[offset + 1]));\n                        }\n                    }\n                    geometries.push_back(factory.createMultiPoint(std::move(vertices)));\n                }\n\n                partOffsetCounter += numPoints;\n                ringOffsetsCounter += numPoints;\n                break;\n            }\n            case GeometryType::LINESTRING: {\n                if (!topologyVector) {\n                    throw std::runtime_error(\"Linestring geometry without topology vector\");\n                }\n\n                std::uint32_t numVertices = 0;\n                if (containsPolygon) {\n                    const auto& ringOffsets = topologyVector->getRingOffsets();\n                    CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                    numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                    ringOffsetsCounter++;\n                } else {\n                    const auto& partOffsets = topologyVector->getPartOffsets();\n                    CHECK_BUFFER(partOffsetCounter, partOffsets);\n                    numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                }\n                partOffsetCounter++;\n\n                std::vector<Coordinate> vertices;\n                if (vertexOffsets.empty()) {\n                    vertices = getLineStringCoords(\n                        vertexBuffer, vertexBufferOffset, numVertices, /*closeLineString=*/false);\n                    vertexBufferOffset += numVertices * 2;\n                } else {\n                    vertices = (vertexBufferType == VertexBufferType::VEC_2)\n                                   ? getDictionaryEncodedLineStringCoords(vertexBuffer,\n                                                                          vertexOffsets,\n                                                                          vertexOffsetsOffset,\n                                                                          numVertices,\n                                                                          /*closeLineString=*/false)\n                                   : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                      vertexOffsets,\n                                                                      vertexOffsetsOffset,\n                                                                      numVertices,\n                                                                      *mortonSettings,\n                                                                      /*closeLineString=*/false);\n                    vertexOffsetsOffset += numVertices;\n                }\n\n                geometryOffsetsCounter++;\n\n                geometries.push_back(factory.createLineString(std::move(vertices)));\n                break;\n            }\n            case GeometryType::POLYGON: {\n                if (!topologyVector) {\n                    throw std::runtime_error(\"Polygon geometry without topology vector\");\n                }\n                [[maybe_unused]] const auto& geometryOffsets = topologyVector->getGeometryOffsets();\n                const auto& partOffsets = topologyVector->getPartOffsets();\n                const auto& ringOffsets = topologyVector->getRingOffsets();\n\n                CHECK_BUFFER(partOffsetCounter, partOffsets);\n                const auto numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                partOffsetCounter++;\n\n                CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                const auto numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                ringOffsetsCounter++;\n\n                std::vector<CoordVec> rings;\n                rings.reserve(numRings);\n\n                auto totalVertices = numVertices;\n                if (vertexOffsets.empty()) {\n                    rings.push_back(\n                        getLineStringCoords(vertexBuffer, vertexBufferOffset, numVertices, /*closeLineString=*/true));\n                    vertexBufferOffset += numVertices * 2;\n                    assert(triangleCounts.empty() || numVertices == rings.back().size() ||\n                           numVertices == rings.back().size() - 1);\n\n                    for (std::uint32_t j = 1; j < numRings; ++j) {\n                        CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                        const auto numRingVertices = ringOffsets[ringOffsetsCounter] -\n                                                     ringOffsets[ringOffsetsCounter - 1];\n                        totalVertices += numRingVertices;\n                        ringOffsetsCounter++;\n                        const auto vbo = vertexBufferOffset;\n                        vertexBufferOffset += numRingVertices * 2;\n                        rings.push_back(\n                            getLineStringCoords(vertexBuffer, vbo, numRingVertices, /*closeLineString=*/true));\n                        assert(triangleCounts.empty() || numRingVertices == rings.back().size() ||\n                               numRingVertices == rings.back().size() - 1);\n                    }\n\n                    auto newGeometry = factory.createPolygon(std::move(rings));\n                    applyTriangles(*newGeometry, triangleOffset, indexBufferOffset, totalVertices, false);\n                    geometries.push_back(std::move(newGeometry));\n                } else {\n                    rings.push_back((vertexBufferType == VertexBufferType::VEC_2)\n                                        ? getDictionaryEncodedLineStringCoords(vertexBuffer,\n                                                                               vertexOffsets,\n                                                                               vertexOffsetsOffset,\n                                                                               numVertices,\n                                                                               /*closeLineString=*/true)\n                                        : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                           vertexOffsets,\n                                                                           vertexOffsetsOffset,\n                                                                           numVertices,\n                                                                           *mortonSettings,\n                                                                           /*closeLineString=*/true));\n                    vertexOffsetsOffset += numVertices;\n                    assert(triangleCounts.empty() || numVertices == rings.back().size() ||\n                           numVertices == rings.back().size() - 1);\n\n                    auto dictTotalVertices = numVertices;\n                    for (std::uint32_t j = 1; j < numRings; ++j) {\n                        CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                        const auto numRingVertices = ringOffsets[ringOffsetsCounter] -\n                                                     ringOffsets[ringOffsetsCounter - 1];\n                        ringOffsetsCounter++;\n\n                        dictTotalVertices += numRingVertices;\n\n                        const auto offset = vertexOffsetsOffset;\n                        vertexOffsetsOffset += numRingVertices;\n\n                        rings.push_back(\n                            (vertexBufferType == VertexBufferType::VEC_2)\n                                ? getDictionaryEncodedLineStringCoords(\n                                      vertexBuffer, vertexOffsets, offset, numRingVertices, /*closeLineString=*/true)\n                                : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                   vertexOffsets,\n                                                                   offset,\n                                                                   numRingVertices,\n                                                                   *mortonSettings,\n                                                                   /*closeLineString=*/true));\n                        assert(triangleCounts.empty() || numRingVertices == rings.back().size());\n                    }\n\n                    auto newGeometry = factory.createPolygon(std::move(rings));\n                    applyTriangles(*newGeometry, triangleOffset, indexBufferOffset, dictTotalVertices, false);\n                    geometries.push_back(std::move(newGeometry));\n                }\n\n                if (topologyVector && !topologyVector->getGeometryOffsets().empty()) {\n                    geometryOffsetsCounter++;\n                }\n                break;\n            }\n            case GeometryType::MULTILINESTRING: {\n                if (!topologyVector) {\n                    throw std::runtime_error(\"Multi-Linestring geometry without topology vector\");\n                }\n                const auto& geometryOffsets = topologyVector->getGeometryOffsets();\n                const auto& partOffsets = topologyVector->getPartOffsets();\n                const auto& ringOffsets = topologyVector->getRingOffsets();\n\n                CHECK_BUFFER(geometryOffsetsCounter, geometryOffsets);\n                const auto numLineStrings = geometryOffsets[geometryOffsetsCounter] -\n                                            geometryOffsets[geometryOffsetsCounter - 1];\n                geometryOffsetsCounter++;\n\n                std::vector<CoordVec> lineStrings;\n                lineStrings.reserve(numLineStrings);\n\n                if (vertexOffsets.empty()) {\n                    for (std::uint32_t j = 0; j < numLineStrings; j++) {\n                        std::uint32_t numVertices = 0;\n                        if (containsPolygon) {\n                            CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                            numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                        } else {\n                            CHECK_BUFFER(partOffsetCounter, partOffsets);\n                            numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        }\n                        partOffsetCounter++;\n\n                        lineStrings.push_back(getLineStringCoords(\n                            vertexBuffer, vertexBufferOffset, numVertices, /*closeLineString=*/false));\n                        vertexBufferOffset += numVertices * 2;\n                    }\n                    geometries.push_back(factory.createMultiLineString(std::move(lineStrings)));\n                } else {\n                    for (std::uint32_t j = 0; j < numLineStrings; ++j) {\n                        std::uint32_t numVertices = 0;\n                        if (containsPolygon) {\n                            CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                            numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                        } else {\n                            CHECK_BUFFER(partOffsetCounter, partOffsets);\n                            numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        }\n                        partOffsetCounter++;\n\n                        lineStrings.push_back((vertexBufferType == VertexBufferType::VEC_2)\n                                                  ? getDictionaryEncodedLineStringCoords(vertexBuffer,\n                                                                                         vertexOffsets,\n                                                                                         vertexOffsetsOffset,\n                                                                                         numVertices,\n                                                                                         /*closeLineString=*/false)\n                                                  : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                                     vertexOffsets,\n                                                                                     vertexOffsetsOffset,\n                                                                                     numVertices,\n                                                                                     *mortonSettings,\n                                                                                     /*closeLineString=*/false));\n                        vertexOffsetsOffset += numVertices;\n                    }\n                    geometries.push_back(factory.createMultiLineString(std::move(lineStrings)));\n                }\n                break;\n            }\n            case GeometryType::MULTIPOLYGON: {\n                if (!topologyVector) {\n                    throw std::runtime_error(\"Multi-polygon geometry without topology vector\");\n                }\n                const auto& geometryOffsets = topologyVector->getGeometryOffsets();\n                const auto& partOffsets = topologyVector->getPartOffsets();\n                const auto& ringOffsets = topologyVector->getRingOffsets();\n\n                CHECK_BUFFER(geometryOffsetsCounter, geometryOffsets);\n                const auto numPolygons = geometryOffsets[geometryOffsetsCounter] -\n                                         geometryOffsets[geometryOffsetsCounter - 1];\n                geometryOffsetsCounter++;\n\n                std::vector<MultiPolygon::RingVec> polygons;\n                polygons.reserve(numPolygons);\n\n                if (vertexOffsets.empty()) {\n                    [[maybe_unused]] std::uint32_t plainTotalVertices = 0;\n                    for (std::uint32_t j = 0; j < numPolygons; ++j) {\n                        CHECK_BUFFER(partOffsetCounter, partOffsets);\n                        const auto numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        partOffsetCounter++;\n\n                        CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                        const auto numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                        ringOffsetsCounter++;\n\n                        plainTotalVertices += numVertices;\n\n                        std::vector<CoordVec> rings;\n                        rings.reserve(numRings);\n\n                        rings.push_back(getLineStringCoords(\n                            vertexBuffer, vertexBufferOffset, numVertices, /*closeLineString=*/true));\n                        vertexBufferOffset += numVertices * 2;\n\n                        for (std::uint32_t k = 1; k < numRings; ++k) {\n                            CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                            const auto numRingVertices = ringOffsets[ringOffsetsCounter] -\n                                                         ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n\n                            plainTotalVertices += numRingVertices;\n\n                            rings.push_back(getLineStringCoords(\n                                vertexBuffer, vertexBufferOffset, numRingVertices, /*closeLineString=*/true));\n                            vertexBufferOffset += numRingVertices * 2;\n                        }\n\n                        polygons.push_back(std::move(rings));\n                    }\n\n                    auto newGeometry = factory.createMultiPolygon(std::move(polygons));\n                    applyTriangles(*newGeometry, triangleOffset, indexBufferOffset, plainTotalVertices, true);\n                    geometries.push_back(std::move(newGeometry));\n                } else {\n                    [[maybe_unused]] std::uint32_t dictTotalVertices = 0;\n                    for (std::uint32_t j = 0; j < numPolygons; ++j) {\n                        CHECK_BUFFER(partOffsetCounter, partOffsets);\n                        const auto numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        partOffsetCounter++;\n\n                        CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                        const auto numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                        ringOffsetsCounter++;\n\n                        dictTotalVertices += numVertices;\n\n                        std::vector<CoordVec> rings;\n                        rings.reserve(numRings);\n\n                        rings.push_back((vertexBufferType == VertexBufferType::VEC_2)\n                                            ? getDictionaryEncodedLineStringCoords(vertexBuffer,\n                                                                                   vertexOffsets,\n                                                                                   vertexOffsetsOffset,\n                                                                                   numVertices,\n                                                                                   /*closeLineString=*/true)\n                                            : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                               vertexOffsets,\n                                                                               vertexOffsetsOffset,\n                                                                               numVertices,\n                                                                               *mortonSettings,\n                                                                               /*closeLineString=*/true));\n                        vertexOffsetsOffset += numVertices;\n\n                        for (std::uint32_t k = 1; k < numRings; ++k) {\n                            CHECK_BUFFER(ringOffsetsCounter, ringOffsets);\n                            const auto numRingVertices = ringOffsets[ringOffsetsCounter] -\n                                                         ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n\n                            dictTotalVertices += numRingVertices;\n\n                            rings.push_back((vertexBufferType == VertexBufferType::VEC_2)\n                                                ? getDictionaryEncodedLineStringCoords(vertexBuffer,\n                                                                                       vertexOffsets,\n                                                                                       vertexOffsetsOffset,\n                                                                                       numRingVertices,\n                                                                                       /*closeLineString=*/true)\n                                                : getMortonEncodedLineStringCoords(vertexBuffer,\n                                                                                   vertexOffsets,\n                                                                                   vertexOffsetsOffset,\n                                                                                   numRingVertices,\n                                                                                   *mortonSettings,\n                                                                                   /*closeLineString=*/true));\n                            vertexOffsetsOffset += numRingVertices;\n                        }\n\n                        polygons.push_back(std::move(rings));\n                    }\n\n                    auto newGeometry = factory.createMultiPolygon(std::move(polygons));\n                    applyTriangles(*newGeometry, triangleOffset, indexBufferOffset, dictTotalVertices, true);\n                    geometries.push_back(std::move(newGeometry));\n                }\n                break;\n            }\n            default:\n                throw std::runtime_error(\"Unsupported geometry type: \" + std::to_string(std::to_underlying(geomType)));\n        }\n    }\n\n    // If we didn't use all the input data, that's a bug.\n    assert(indexBufferOffset == indexBuffer.size());\n    assert(triangleOffset == triangleCounts.size());\n\n    // TODO: this fails for `test/expected/amazon_here/8_132_85.mlt`\n    // assert(!topologyVector || topologyVector->getPartOffsets().empty() ||\n    //        partOffsetCounter == topologyVector->getPartOffsets().size());\n    assert(!topologyVector || topologyVector->getRingOffsets().empty() ||\n           ringOffsetsCounter == topologyVector->getRingOffsets().size());\n    assert(!topologyVector || topologyVector->getGeometryOffsets().empty() ||\n           geometryOffsetsCounter == topologyVector->getGeometryOffsets().size());\n\n    // If we're using vertex offsets, we should have used all\n    // of them, and we won't have used vertex buffer offsets.\n    assert(!vertexOffsets.empty() || vertexBufferOffset == vertexBuffer.size());\n    assert(vertexOffsets.empty() || vertexOffsetsOffset == vertexOffsets.size());\n\n    return geometries;\n}\n\n} // namespace mlt::geometry\n"
  },
  {
    "path": "cpp/src/mlt/layer.cpp",
    "content": "#include <mlt/layer.hpp>\n#include <mlt/geometry_vector.hpp>\n\nnamespace mlt {\n\nLayer::Layer(std::string name_,\n             extent_t extent_,\n             std::unique_ptr<geometry::GeometryVector>&& geometryVector_,\n             std::vector<Feature> features_,\n             PropertyVecMap properties_) noexcept\n    : name(std::move(name_)),\n      extent(extent_),\n      geometryVector(std::move(geometryVector_)),\n      features(std::move(features_)),\n      properties(std::move(properties_)) {}\n\nLayer::~Layer() = default;\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/src/mlt/metadata/stream.cpp",
    "content": "#include <mlt/metadata/stream.hpp>\n\n#include <string>\n#include <utility>\n\nnamespace mlt::metadata::stream {\n\nnamespace {\nstd::optional<LogicalStreamType> decodeLogicalStreamType(PhysicalStreamType physicalStreamType, int value) {\n    switch (physicalStreamType) {\n        case PhysicalStreamType::DATA: {\n            const auto type = static_cast<DictionaryType>(value);\n            if (type < DictionaryType::VALUE_COUNT) {\n                return type;\n            }\n            break;\n        }\n        case PhysicalStreamType::OFFSET: {\n            const auto type = static_cast<OffsetType>(value);\n            if (type < OffsetType::VALUE_COUNT) {\n                return type;\n            }\n            break;\n        }\n        case PhysicalStreamType::LENGTH: {\n            const auto type = static_cast<LengthType>(value);\n            if (type < LengthType::VALUE_COUNT) {\n                return type;\n            }\n            break;\n        }\n        case PhysicalStreamType::PRESENT:\n            return {};\n        default:\n            break;\n    }\n    throw std::runtime_error(\"Invalid logical stream type: \" + std::to_string(std::to_underlying(physicalStreamType)));\n}\n} // namespace\n\nstd::unique_ptr<StreamMetadata> StreamMetadata::decode(BufferStream& tileData) {\n    auto streamMetadata = decodeInternal(tileData);\n\n    // Currently Morton can't be combined with RLE only with delta\n    if (streamMetadata.getLogicalLevelTechnique1() == LogicalLevelTechnique::MORTON) {\n        auto result = MortonEncodedStreamMetadata::decodePartial(std::move(streamMetadata), tileData);\n        return std::make_unique<MortonEncodedStreamMetadata>(std::move(result));\n    }\n    // Boolean RLE doesn't need additional information\n    else if ((streamMetadata.getLogicalLevelTechnique1() == LogicalLevelTechnique::RLE ||\n              streamMetadata.getLogicalLevelTechnique2() == LogicalLevelTechnique::RLE) &&\n             streamMetadata.getPhysicalLevelTechnique() != PhysicalLevelTechnique::NONE) {\n        auto result = RleEncodedStreamMetadata::decodePartial(std::move(streamMetadata), tileData);\n        return std::make_unique<RleEncodedStreamMetadata>(std::move(result));\n    }\n    return std::make_unique<StreamMetadata>(std::move(streamMetadata));\n}\n\nint StreamMetadata::getLogicalType() const noexcept {\n    if (logicalStreamType) {\n        if (logicalStreamType->getDictionaryType()) {\n            return std::to_underlying(*logicalStreamType->getDictionaryType());\n        }\n\n        if (logicalStreamType->getLengthType()) {\n            return std::to_underlying(*logicalStreamType->getLengthType());\n        }\n\n        if (logicalStreamType->getOffsetType()) {\n            return std::to_underlying(*logicalStreamType->getOffsetType());\n        }\n    }\n    return 0;\n}\n\nStreamMetadata StreamMetadata::decodeInternal(BufferStream& tileData) {\n    const auto streamType = tileData.read();\n    const auto physicalStreamType = static_cast<PhysicalStreamType>(streamType >> 4);\n    auto logicalStreamType = decodeLogicalStreamType(physicalStreamType, streamType & 0x0f);\n\n    const auto encodingsHeader = tileData.read() & 0xff;\n    const auto logicalLevelTechnique1 = static_cast<LogicalLevelTechnique>(encodingsHeader >> 5);\n    const auto logicalLevelTechnique2 = static_cast<LogicalLevelTechnique>((encodingsHeader >> 2) & 0x7);\n    const auto physicalLevelTechnique = static_cast<PhysicalLevelTechnique>(encodingsHeader & 0x3);\n\n    if (physicalStreamType >= PhysicalStreamType::VALUE_COUNT ||\n        logicalLevelTechnique1 >= LogicalLevelTechnique::VALUE_COUNT ||\n        logicalLevelTechnique2 >= LogicalLevelTechnique::VALUE_COUNT ||\n        physicalLevelTechnique >= PhysicalLevelTechnique::VALUE_COUNT) {\n        throw std::runtime_error(\"Invalid stream encoding\");\n    }\n\n    using namespace util::decoding;\n    const auto [numValues, byteLength] = decodeVarints<std::uint32_t, 2>(tileData);\n\n    return {\n        physicalStreamType,\n        std::move(logicalStreamType),\n        logicalLevelTechnique1,\n        logicalLevelTechnique2,\n        physicalLevelTechnique,\n        numValues,\n        byteLength,\n    };\n}\n\n} // namespace mlt::metadata::stream\n"
  },
  {
    "path": "cpp/src/mlt/metadata/tileset.cpp",
    "content": "#include <mlt/metadata/tileset.hpp>\n#include <mlt/metadata/type_map.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/stl.hpp>\n#include <mlt/util/varint.hpp>\n\n#include <utility>\n\nnamespace mlt::metadata::tileset {\nusing util::decoding::decodeVarint;\n\nnamespace {\nstd::string decodeString(BufferStream& tileData) {\n    std::string result;\n    const auto length = decodeVarint<std::uint32_t>(tileData);\n    if (length > 0) {\n        result.resize(length);\n        tileData.read(result.data(), length);\n    }\n    return result;\n}\n\nColumn decodeColumn(BufferStream& tileData) {\n    const auto typeCode = decodeVarint<std::uint32_t>(tileData);\n    auto column = type_map::Tag0x01::decodeColumnType(typeCode);\n    if (!column) {\n        // We can't just skip this because we don't know the actual length\n        throw std::runtime_error(\"Unsupported column type code: \" + std::to_string(typeCode));\n    }\n\n    if (type_map::Tag0x01::columnTypeHasName(typeCode)) {\n        column->name = decodeString(tileData);\n    }\n\n    if (type_map::Tag0x01::columnTypeHasChildren(typeCode)) {\n        assert(column->hasComplexType());\n        auto& complex = column->getComplexType();\n        const auto childCount = decodeVarint<std::uint32_t>(tileData);\n        complex.children = util::generateVector<Column>(childCount, [&](auto) { return decodeColumn(tileData); });\n    }\n\n    return *column;\n}\n} // namespace\n\nFeatureTable decodeFeatureTable(BufferStream& tileData) {\n    auto name = decodeString(tileData);\n    const auto extent = decodeVarint<std::uint32_t>(tileData);\n    const auto columnCount = decodeVarint<std::uint32_t>(tileData);\n    auto columns = util::generateVector<Column>(columnCount, [&](auto) { return decodeColumn(tileData); });\n    return {.name = std::move(name), .extent = extent, .columns = std::move(columns)};\n}\n\n} // namespace mlt::metadata::tileset\n"
  },
  {
    "path": "cpp/src/mlt/properties.cpp",
    "content": "#include <mlt/properties.hpp>\n\n#include <mlt/util/packed_bitset.hpp>\n#include <mlt/util/stl.hpp>\n\n#include <cassert>\n#include <limits>\n#include <stdexcept>\n#include <variant>\n\nnamespace mlt {\n\nnamespace {\n\n// Visitor to extract a single `Property` from each type of `PropertyVec`\nstruct ExtractPropertyVisitor {\n    ExtractPropertyVisitor(std::size_t i_, bool byteIsBooleans_)\n        : i(i_),\n          byteIsBooleans(byteIsBooleans_) {}\n\n    template <typename T>\n    std::optional<Property> operator()(const T& vec) const;\n\n    template <typename T>\n    std::optional<Property> operator()(const std::vector<T>& vec) const {\n        assert(i < vec.size());\n        return (i < vec.size()) ? std::optional<Property>{vec[i]} : std::nullopt;\n    }\n\n    std::optional<Property> operator()(const std::vector<std::uint8_t>& vec) const {\n        if (byteIsBooleans) {\n            assert(i < 8 * vec.size());\n            return testBit(vec, i);\n        }\n        assert(i < vec.size());\n        return (i < vec.size()) ? std::optional<Property>{static_cast<std::uint32_t>(vec[i])} : std::nullopt;\n    }\n\nprivate:\n    const std::size_t i;\n    const bool byteIsBooleans;\n};\n\ntemplate <>\nstd::optional<Property> ExtractPropertyVisitor::operator()(const StringDictViews& views) const {\n    const auto& strings = views.getStrings();\n    assert(i < strings.size());\n    return (i < strings.size()) ? std::optional<Property>{strings[i]} : std::nullopt;\n}\n\nauto getPropertyValue(const PropertyVec& layerProperties, std::size_t sourceIndex, bool isBoolean) {\n    const auto value = std::visit(ExtractPropertyVisitor(sourceIndex, isBoolean), layerProperties);\n    if (value) {\n        return *value;\n    } else {\n        throw std::runtime_error(\"Missing property value\");\n    }\n};\n\ntemplate <typename T>\nstd::vector<T> buildIndexVector(const PackedBitset& present) {\n    std::vector<T> indexes;\n    indexes.reserve(8 * present.size());\n    T curPhysicalIndex = 0;\n    constexpr auto nullIndex = std::numeric_limits<T>::max();\n    for (const auto bits : present) {\n        for (std::uint8_t i = 0; i < 8; ++i) {\n            indexes.push_back((bits & (1 << i)) ? curPhysicalIndex++ : nullIndex);\n        }\n    }\n    return indexes;\n}\n} // namespace\n\nPresentProperties::PresentProperties(ScalarType type_, PropertyVec properties_, const PackedBitset& present) noexcept\n    : type(type_),\n      properties(std::move(properties_)) {\n    if (!present.empty()) {\n        if (8 * present.size() < std::numeric_limits<std::uint16_t>::max()) {\n            physicalIndexes = buildIndexVector<std::uint16_t>(present);\n        } else {\n            physicalIndexes = buildIndexVector<std::uint32_t>(present);\n        }\n    }\n}\n\nstd::optional<Property> PresentProperties::getProperty(std::uint32_t logicalIndex) const {\n    using OptIndex = std::optional<std::uint32_t>;\n    const auto index = std::visit(\n        util::overloaded{[&](const std::monostate&) -> OptIndex {\n                             // not an optional property, physical index is the same as\n                             // logical index\n                             return logicalIndex;\n                         },\n                         [&](const auto& indexes) {\n                             if (logicalIndex < indexes.size()) {\n                                 const auto idx = indexes[logicalIndex];\n                                 return (idx < std::numeric_limits<decltype(idx)>::max()) ? OptIndex{idx} : OptIndex{};\n                             }\n                             return OptIndex{};\n                         }},\n        physicalIndexes);\n\n    return index ? getPropertyValue(properties, *index, isBoolean()) : std::optional<Property>{};\n}\n\n} // namespace mlt\n"
  },
  {
    "path": "cpp/src/mlt/util/json_diff.hpp",
    "content": "#pragma once\n#include <cstddef>\n#if MLT_WITH_JSON\n\n#include <mlt/json.hpp>\n\n#include <nlohmann/detail/string_concat.hpp>\n\nnamespace mlt::util {\nusing json = nlohmann::json;\n\n// Comparison functor that can compare stringified numbers and consider precision\nstruct JSONComparer {\n    const double doubleEpsilon = 1.0e-15;\n    bool operator()(const json& left, const json& right) const {\n        const auto leftDouble = getDouble(left);\n        const auto rightDouble = getDouble(right);\n\n        // Very similar numbers are equivalent, even if one or both are in string form\n        if (leftDouble && rightDouble) {\n            const auto md = (*leftDouble + *rightDouble) / 2;\n            return (std::fabs(*leftDouble - *rightDouble) / ((md == 0) ? 1 : md)) < doubleEpsilon;\n        }\n\n        // Missing entries are equivalent to numeric zeros\n        if ((left.is_null() && rightDouble == 0) || (right.is_null() && leftDouble == 0)) {\n            return true;\n        }\n\n        // Empty objects and arrays are equivalent to missing entries\n        if (((left.is_object() || left.is_array()) && left.empty() && right.is_null()) ||\n            (left.is_null() && (right.is_object() || right.is_array()) && right.empty())) {\n            return true;\n        }\n        return (left == right);\n    }\n\nprivate:\n    static std::optional<double> getDouble(const json& v) {\n        switch (v.type()) {\n            case nlohmann::json_abi_v3_11_3::detail::value_t::null:\n                return 0;\n            case nlohmann::json_abi_v3_11_3::detail::value_t::number_integer:\n            case nlohmann::json_abi_v3_11_3::detail::value_t::number_unsigned:\n            case nlohmann::json_abi_v3_11_3::detail::value_t::number_float:\n                return v.get<double>();\n            case nlohmann::json_abi_v3_11_3::detail::value_t::string: {\n                const auto s = v.get<std::string>();\n                char* end = nullptr;\n                const auto d = std::strtof(s.c_str(), &end);\n                if (end - s.c_str() == static_cast<std::ptrdiff_t>(s.size())) {\n                    return d;\n                }\n                return {}; // not a number\n            }\n            default:\n                return {};\n        }\n    }\n};\n\n/// Based on `nlohmann:json::diff` but with a custom comparator and puts the old/expected value in the result\nstatic json diff(const json& source,\n                 const json& target,\n                 const std::string& path = {},\n                 std::function<bool(const json&, const json&)> compare = JSONComparer()) {\n    using namespace nlohmann;\n    using nlohmann::detail::concat;\n    using nlohmann::detail::escape;\n\n    // the patch\n    json result(json::value_t::array);\n\n    // if the values are the same, return empty patch\n    if (compare(source, target)) {\n        return result;\n    }\n\n    if (source.type() != target.type()) {\n        // different types: replace value\n        result.push_back({{\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}, {\"original\", source}});\n        return result;\n    }\n\n    switch (source.type()) {\n        case json::value_t::array: {\n            // first pass: traverse common elements\n            std::size_t i = 0;\n            while (i < source.size() && i < target.size()) {\n                // recursive call to compare array values at index i\n                auto temp_diff = diff(source[i], target[i], concat(path, '/', std::to_string(i)));\n                result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                ++i;\n            }\n\n            // We now reached the end of at least one array\n            // in a second pass, traverse the remaining elements\n\n            // remove my remaining elements\n            const auto end_index = static_cast<json::difference_type>(result.size());\n            while (i < source.size()) {\n                // add operations in reverse order to avoid invalid\n                // indices\n                result.insert(\n                    result.begin() + end_index,\n                    json::object(\n                        {{\"op\", \"remove\"}, {\"path\", concat(path, '/', std::to_string(i))}, {\"value\", source[i]}}));\n                ++i;\n            }\n\n            // add other remaining elements\n            while (i < target.size()) {\n                result.push_back({{\"op\", \"add\"}, {\"path\", concat(path, \"/-\")}, {\"value\", target[i]}});\n                ++i;\n            }\n\n            break;\n        }\n\n        case json::value_t::object: {\n            // first pass: traverse this object's elements\n            for (auto it = source.cbegin(); it != source.cend(); ++it) {\n                // escape the key name to be used in a JSON patch\n                const auto path_key = concat(path, '/', escape(it.key()));\n\n                if (target.find(it.key()) != target.end()) {\n                    // recursive call to compare object values at key it\n                    auto temp_diff = diff(it.value(), target[it.key()], path_key);\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                } else {\n                    // found a key that is not in o.\n                    // If the value is equivalent to nothing, that's not a difference.\n                    const auto& sourceValue = source[it.key()];\n                    if (!compare(sourceValue, json())) {\n                        result.push_back(json::object({{\"op\", \"remove\"}, {\"path\", path_key}, {\"value\", sourceValue}}));\n                    }\n                }\n            }\n\n            // second pass: traverse other object's elements\n            for (auto it = target.cbegin(); it != target.cend(); ++it) {\n                if (source.find(it.key()) == source.end()) {\n                    // found a key that is not in this\n                    // If the value is equivalent to nothing, that's not a difference.\n                    if (!compare(json(), *it)) {\n                        const auto path_key = concat(path, '/', escape(it.key()));\n                        result.push_back({{\"op\", \"add\"}, {\"path\", path_key}, {\"value\", *it}});\n                    }\n                }\n            }\n\n            break;\n        }\n\n        case json::value_t::null:\n        case json::value_t::string:\n        case json::value_t::boolean:\n        case json::value_t::number_integer:\n        case json::value_t::number_unsigned:\n        case json::value_t::number_float:\n        case json::value_t::binary:\n        case json::value_t::discarded:\n        default: {\n            // both primitive type: replace value\n            result.push_back({{\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}, {\"original\", source}});\n            break;\n        }\n    }\n\n    return result;\n}\n\n} // namespace mlt::util\n#endif // MLT_WITH_JSON\n"
  },
  {
    "path": "cpp/src/mlt/util/morton_curve.hpp",
    "content": "#pragma once\n\n#include <mlt/util/space_filling_curve.hpp>\n\nnamespace mlt::util {\n\nclass MortonCurve : public SpaceFillingCurve {\npublic:\n    MortonCurve(std::int32_t minVertexValue, std::int32_t maxVertexValue)\n        : SpaceFillingCurve(minVertexValue, maxVertexValue) {}\n\n    std::uint32_t encode(const Coordinate& vertex) const override {\n        validate(vertex);\n        return encodeMorton(vertex, numBits, coordinateShift);\n    }\n\n    Coordinate decode(std::uint32_t mortonCode) const noexcept override {\n        return decode(mortonCode, numBits, coordinateShift);\n    }\n\n    static Coordinate decode(std::uint32_t mortonCode, std::uint32_t numBits, std::int32_t coordinateShift) noexcept {\n        return {static_cast<float>(decode(mortonCode, numBits) - coordinateShift),\n                static_cast<float>(decode(mortonCode >> 1, numBits) - coordinateShift)};\n    }\n\n    static std::int32_t decode(std::uint32_t code, std::uint32_t numBits) noexcept {\n        std::uint32_t coordinate = 0;\n        for (std::uint32_t i = 0; i < numBits; ++i) {\n            coordinate |= (code & (1ul << (2 * i))) >> i;\n        }\n        return static_cast<std::int32_t>(coordinate);\n    }\n\nprivate:\n    static std::uint32_t encodeMorton(const Coordinate& vertex,\n                                      std::uint32_t numBits,\n                                      std::int32_t coordinateShift) noexcept {\n        const auto shiftedX = static_cast<std::uint32_t>(vertex.x + coordinateShift);\n        const auto shiftedY = static_cast<std::uint32_t>(vertex.y + coordinateShift);\n        std::uint32_t mortonCode = 0;\n        for (std::uint32_t i = 0; i < numBits; ++i) {\n            const auto mask = 1u << i;\n            mortonCode |= ((shiftedX & mask) << i) | ((shiftedY & mask) << (i + 1));\n        }\n        return mortonCode;\n    }\n};\n\n} // namespace mlt::util\n"
  },
  {
    "path": "cpp/src/mlt/util/raw.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n#include <mlt/metadata/stream.hpp>\n\n#include <algorithm>\n#include <cassert>\n\nnamespace mlt::util::decoding {\n\ninline void decodeRaw(BufferStream& tileData, std::vector<std::uint8_t>& out, std::uint32_t numBytes, bool consume) {\n    out.resize(numBytes);\n    std::copy(tileData.getReadPosition(), tileData.getReadPosition() + numBytes, out.begin());\n    if (consume) {\n        tileData.consume(numBytes);\n    }\n}\n\ntemplate <typename T>\nvoid decodeRaw(BufferStream& tileData,\n               std::vector<T>& out,\n               const metadata::stream::StreamMetadata& metadata,\n               bool consume) {\n    const auto numValues = metadata.getNumValues();\n    const auto numBytes = static_cast<std::uint32_t>(sizeof(T) * numValues);\n    assert(numBytes == metadata.getByteLength());\n    out.resize(numValues);\n    std::copy(\n        tileData.getReadPosition(), tileData.getReadPosition() + numBytes, reinterpret_cast<std::uint8_t*>(out.data()));\n    if (consume) {\n        tileData.consume(numBytes);\n    }\n}\n\n} // namespace mlt::util::decoding\n"
  },
  {
    "path": "cpp/src/mlt/util/rle.cpp",
    "content": "#include <mlt/util/rle.hpp>\n\n#include <mlt/metadata/stream.hpp>\n\n#include <cassert>\n\nnamespace mlt::util::decoding::rle {\n\nvoid decodeBoolean(BufferStream& tileData,\n                   std::vector<uint8_t>& out,\n                   const metadata::stream::StreamMetadata& metadata,\n                   bool consume) {\n    const auto bitCount = metadata.getNumValues();\n    const auto numBytes = (bitCount + 7) / 8;\n    out.resize(numBytes);\n\n    assert(metadata.getByteLength() <= tileData.getRemaining());\n    detail::ByteRleDecoder decoder{tileData.getReadPosition(),\n                                   std::min<std::size_t>(metadata.getByteLength(), tileData.getRemaining())};\n    decoder.next(out.data(), numBytes);\n    assert(decoder.getBufferRemaining() == 0);\n\n    if (consume) {\n        tileData.consume(metadata.getByteLength());\n    }\n}\n\n} // namespace mlt::util::decoding::rle\n"
  },
  {
    "path": "cpp/src/mlt/util/rle.hpp",
    "content": "#pragma once\n\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/noncopyable.hpp>\n\n#include <algorithm>\n#include <functional>\n#include <type_traits>\n\nnamespace mlt::metadata::stream {\nclass StreamMetadata;\n}\n\nnamespace mlt::util::decoding::rle {\n\nnamespace detail {\n// Borrowed from https://github.com/apache/orc, `/c++/src/ByteRLE.cc`, `ByteRleDecoderImpl::nextInternal`\n// Apache License 2.0\nclass ByteRleDecoder : public util::noncopyable {\npublic:\n    ByteRleDecoder() = delete;\n    ByteRleDecoder(const std::uint8_t* buffer, size_t length) noexcept\n        : bufferStart(reinterpret_cast<const char*>(buffer)),\n          bufferEnd(bufferStart + length) {}\n    ByteRleDecoder(ByteRleDecoder&&) = delete;\n    ByteRleDecoder& operator=(const ByteRleDecoder&) = delete;\n    ByteRleDecoder& operator=(ByteRleDecoder&&) = delete;\n\n    std::size_t getBufferRemaining() const noexcept { return bufferEnd - bufferStart; }\n\n    void next(std::uint8_t* data, std::uint64_t numValues) {\n        std::uint64_t position = 0;\n\n        while (position < numValues) {\n            // if we are out of values, read more\n            if (remainingValues == 0) {\n                readHeader();\n            }\n            // how many do we read out of this block?\n            size_t count = std::min(static_cast<size_t>(numValues - position), remainingValues);\n            uint64_t consumed = 0;\n            if (repeating) {\n                std::fill_n(data + position, count, value);\n                consumed = count;\n            } else {\n                uint64_t i = 0;\n                while (i < count) {\n                    uint64_t copyBytes = std::min(static_cast<uint64_t>(count - i),\n                                                  static_cast<uint64_t>(bufferEnd - bufferStart));\n                    std::copy(bufferStart, bufferStart + copyBytes, data + position + i);\n                    if (!copyBytes) {\n                        // prevent infinite loop\n                        throw std::runtime_error(\"Unexpected end of buffer\");\n                    }\n                    bufferStart += copyBytes;\n                    i += copyBytes;\n                }\n                consumed = count;\n            }\n            remainingValues -= consumed;\n            position += count;\n        }\n    }\n\nprivate:\n    char readByte() {\n        if (bufferStart == bufferEnd) {\n            throw std::runtime_error(\"Unexpected end of buffer\");\n        }\n        return *bufferStart++;\n    }\n\n    void readHeader() {\n        const std::uint8_t ch = readByte();\n        if (ch & (1 << 7)) {\n            remainingValues = (ch ^ 0xff) + 1;\n            repeating = false;\n        } else {\n            remainingValues = ch + MINIMUM_REPEAT;\n            repeating = true;\n            value = readByte();\n        }\n    }\n\n    static constexpr size_t MINIMUM_REPEAT = 3;\n    size_t remainingValues = 0;\n    char value = 0;\n    const char* bufferStart;\n    const char* bufferEnd;\n    bool repeating = false;\n};\n} // namespace detail\n\n/// Decode RLE bytes to a byte array\n/// @param tileData The source data\n/// @param out The target for output\n/// @param numBytes The number of bytes to write, and the size of `out`\n/// @param byteSize The number of bytes to consume from the source buffer\n/// @throws std::runtime_error The provided buffer does not contain enough data\ninline void decodeByte(BufferStream& tileData, std::uint8_t* out, std::uint32_t numBytes, std::uint32_t byteSize) {\n    detail::ByteRleDecoder{tileData.getData(), tileData.getSize()}.next(out, numBytes);\n    tileData.consume(byteSize);\n}\n\n/// Decode RLE bits to a vector\n/// @param consume Whether to consume input the source buffer (true) or leave it unchanged (false)\nvoid decodeBoolean(BufferStream&, std::vector<uint8_t>&, const metadata::stream::StreamMetadata&, bool consume);\n\n/// Decode RLE bits to int/long values\n/// @param in The source data\n/// @param out The target for output, must already be sized to contain the resulting data\n/// @param numRuns The number of RLE runs in the input\n/// @throws std::runtime_error The provided buffer does not contain enough data\ntemplate <typename T, typename TTarget = T, typename F = std::function<TTarget(T)>>\n    requires(std::is_integral_v<T> && (std::is_integral_v<TTarget> || std::is_enum_v<TTarget>) &&\n             sizeof(T) <= sizeof(TTarget) && std::regular_invocable<F, T> &&\n             std::is_same_v<std::invoke_result_t<F, T>, TTarget>)\nvoid decodeInt(\n    const T* const in,\n    const std::size_t inCount,\n    TTarget* const out,\n    const std::size_t outCount,\n    const std::uint32_t numRuns,\n    std::function<TTarget(T)> convert = [](T x) { return static_cast<TTarget>(x); }) {\n    std::uint32_t inOffset = 0;\n    std::uint32_t outOffset = 0;\n    for (std::uint32_t i = 0; i < numRuns; ++i) {\n        if (inCount < inOffset + 2) {\n            throw std::runtime_error(\"Unexpected end of buffer\");\n        }\n        const T runLength = in[inOffset];\n        const TTarget runValue = convert(in[inOffset++ + numRuns]);\n        if (outCount < outOffset + runLength) {\n            throw std::runtime_error(\"Unexpected end of buffer\");\n        }\n\n        std::fill_n(std::next(out, outOffset), runLength, runValue);\n        outOffset += runLength;\n    }\n}\n\n} // namespace mlt::util::decoding::rle\n"
  },
  {
    "path": "cpp/src/mlt/util/space_filling_curve.hpp",
    "content": "#pragma once\n\n#include <mlt/coordinate.hpp>\n\n#include <cmath>\n#include <stdexcept>\n\nnamespace mlt::util {\n\nclass SpaceFillingCurve {\npublic:\n    SpaceFillingCurve(std::int32_t minVertexValue, std::int32_t maxVertexValue)\n        : coordinateShift((minVertexValue < 0) ? std::abs(minVertexValue) : 0),\n          tileExtent(maxVertexValue + coordinateShift),\n          numBits(static_cast<std::uint32_t>(std::ceil(std::log2(tileExtent + 1)))),\n          minBound(minVertexValue),\n          maxBound(maxVertexValue) {\n        // TODO: fix tile buffer problem\n    }\n    virtual ~SpaceFillingCurve() = default;\n\n    virtual std::uint32_t encode(const Coordinate& vertex) const = 0;\n\n    virtual Coordinate decode(std::uint32_t mortonCode) const = 0;\n\n    std::uint32_t getNumBits() const noexcept { return numBits; }\n\n    std::int32_t getCoordinateShift() const noexcept { return coordinateShift; }\n\nprotected:\n    void validate(const Coordinate& vertex) const {\n        // TODO: also check for int overflow as we are limiting the sfc ids to max int size\n        if (vertex.x < static_cast<float>(minBound) || vertex.y < static_cast<float>(minBound) ||\n            vertex.x > static_cast<float>(maxBound) || vertex.y > static_cast<float>(maxBound)) {\n            throw std::runtime_error(\"The specified tile buffer size is currently not supported\");\n        }\n    }\n\nprotected:\n    const std::int32_t coordinateShift;\n    const std::uint32_t tileExtent;\n    const std::uint32_t numBits;\n    const std::int32_t minBound;\n    const std::int32_t maxBound;\n};\n\n} // namespace mlt::util\n"
  },
  {
    "path": "cpp/src/mlt/util/vectorized.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n\n#include <cassert>\n#include <type_traits>\n\nnamespace mlt::util::decoding::vectorized {\nnamespace {\n/// JS/Java `>>>`\ntemplate <int bits, typename T>\n    requires(std::integral<T>)\nT unsigned_rshift(T t) {\n    return static_cast<T>(static_cast<std::make_unsigned_t<T>>(t) >> bits);\n}\n\n/// Shifts the ones bit into the sign, for reasons\ntemplate <typename T>\n    requires(std::integral<T>)\nT odd_sign(T t) {\n    return (t << 31) >> 31;\n}\n\ntemplate <typename T>\n    requires(std::integral<T>)\nT shift_and_xor(T t) {\n    return unsigned_rshift<1>(t) ^ odd_sign(t);\n}\n\n} // namespace\n\ntemplate <typename T>\n    requires(std::is_integral_v<T> && sizeof(T) == 4)\ninline void decodeComponentwiseDeltaVec2(T* const data, const std::size_t count) noexcept {\n    assert((count % 2) == 0);\n    if (1 < count) {\n        data[0] = shift_and_xor(data[0]);\n        data[1] = shift_and_xor(data[1]);\n        for (std::size_t i = 2; i + 1 < count; i += 2) {\n            data[i] = shift_and_xor(data[i]) + data[i - 2];\n            data[i + 1] = shift_and_xor(data[i + 1]) + data[i - 1];\n        }\n    }\n}\n\n} // namespace mlt::util::decoding::vectorized\n"
  },
  {
    "path": "cpp/src/mlt/util/zigzag.hpp",
    "content": "#pragma once\n\n#include <mlt/common.hpp>\n\n#include <type_traits>\n\nnamespace mlt::util::decoding {\n\ntemplate <typename T>\n    requires(std::is_integral_v<underlying_type_t<T>>)\nT decodeZigZag(T encoded) noexcept {\n    using UT = underlying_type_t<T>;\n    const auto signedValue = static_cast<std::make_signed_t<UT>>(encoded);\n    const auto unsignedValue = static_cast<std::make_unsigned_t<UT>>(encoded);\n    return static_cast<T>((unsignedValue >> 1) ^ (-(signedValue & 1)));\n}\n\n} // namespace mlt::util::decoding\n"
  },
  {
    "path": "cpp/test/CMakeLists.txt",
    "content": "set(TEST_SOURCES\n    test_decode.cpp\n    test_fsst.cpp\n    test_packed_bitset.cpp\n    test_util.cpp\n    test_varint.cpp\n)\n\nif (MLT_WITH_FASTPFOR)\n    list(APPEND TEST_SOURCES test_fastpfor.cpp)\nendif()\n\nadd_executable(mlt-cpp-test ${TEST_SOURCES})\nset_property(TARGET mlt-cpp-test PROPERTY CXX_STANDARD 20)\n\n# Tests have access to private headers\ntarget_include_directories(mlt-cpp-test PUBLIC \"${PROJECT_SOURCE_DIR}/src\")\n\noption(MLT_WITH_JSON \"Include JSON support\" ON)\n\nif(MLT_WITH_JSON)\n    target_include_directories(mlt-cpp-test PUBLIC \"${json_SOURCE_DIR}/include\")\n    target_compile_definitions(mlt-cpp-test PUBLIC MLT_WITH_JSON=1)\nendif(MLT_WITH_JSON)\nif(MLT_WITH_FASTPFOR)\n    target_include_directories(mlt-cpp-test PRIVATE SYSTEM \"${PROJECT_SOURCE_DIR}/vendor/fastpfor/headers\")\nendif(MLT_WITH_FASTPFOR)\n\nset(BUILD_GMOCK OFF CACHE BOOL \"\" FORCE)\nset(INSTALL_GTEST OFF CACHE BOOL \"\" FORCE)\n\n# For Windows: Prevent overriding the parent project's compiler/linker settings\nset(gtest_force_shared_crt ON CACHE BOOL \"\" FORCE)\n\nadd_subdirectory(\"${PROJECT_SOURCE_DIR}/vendor/googletest\" \"${CMAKE_CURRENT_BINARY_DIR}/googletest\" EXCLUDE_FROM_ALL SYSTEM)\n\ntarget_link_libraries(mlt-cpp-test mlt-cpp gtest_main)\n\ninclude(GoogleTest)\n\ngtest_discover_tests(\n    mlt-cpp-test\n    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}\n)\n"
  },
  {
    "path": "cpp/test/test_decode.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <mlt/decoder.hpp>\n#include <mlt/geometry.hpp>\n#include <mlt/metadata/tileset.hpp>\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/projection.hpp>\n\n#include <cstdlib>\n#include <filesystem>\n#include <fstream>\n#include <iostream>\n#include <regex>\n#include <string>\n\nusing namespace std::string_literals;\nusing namespace std::string_view_literals;\n\n#if MLT_WITH_JSON\n#include <mlt/json.hpp>\n#include <mlt/util/json_diff.hpp>\n#endif\n\nnamespace {\nstd::vector<std::filesystem::path> findFiles(const std::filesystem::path& base, const std::regex& pattern) {\n    std::vector<std::filesystem::path> results;\n    for (const auto& entry : std::filesystem::directory_iterator(base)) {\n        std::smatch match;\n        const auto fileName = entry.path().filename().string();\n        if (entry.is_regular_file() && std::regex_match(fileName, match, pattern)) {\n            results.push_back(entry.path());\n        }\n    }\n    return results;\n}\n\nstd::vector<std::ifstream::char_type> loadFile(const std::filesystem::path& path) {\n    std::ifstream file(path, std::ios::binary | std::ios::ate);\n    if (file.is_open()) {\n        const std::size_t size = file.tellg();\n        file.seekg(0);\n\n        std::vector<std::ifstream::char_type> buffer(size);\n        if (file.read(buffer.data(), size)) {\n            return buffer;\n        }\n    }\n    return {};\n}\n\nbool writeFile(const std::filesystem::path& path, const std::string& data) {\n    std::ofstream file(path, std::ios::binary | std::ios::out);\n    if (file.is_open()) {\n        file.write(data.data(), data.size());\n        return !file.fail();\n    }\n    return false;\n}\n\nconst auto basePath = std::filesystem::path(\"../test/expected/tag0x01\");\n\n#if MLT_WITH_JSON\nauto dump(const nlohmann::json& json) {\n    return json.dump(2, ' ', false, nlohmann::json::error_handler_t::replace);\n}\n#endif\n\nstd::pair<std::optional<mlt::MapLibreTile>, std::string> loadTile(const std::string& path) {\n    const auto tileData = loadFile(path);\n    if (tileData.empty()) {\n        return {std::nullopt, \"Failed to read tile data\"};\n    }\n\n    constexpr bool supportFastPFOR = true;\n    auto tile = mlt::Decoder(supportFastPFOR).decode({(tileData.data()), tileData.size()});\n\n#if MLT_WITH_JSON\n    // Load the GeoJSON file, if present\n    auto jsonBuffer = loadFile(path + \".geojson\");\n    if (!jsonBuffer.empty()) {\n        using json = nlohmann::json;\n        const json expectedJSON = json::parse(\n            jsonBuffer, nullptr, /*allow_exceptions=*/false, /*ignore_comments=*/true);\n\n        // Convert the tile we loaded to GeoJSON\n        const auto actualJSON = mlt::json::toJSON(tile, {.x = 3, .y = 5, .z = 7}, true);\n\n        // Compare the two\n        const auto diffJSON = mlt::util::diff(expectedJSON, actualJSON, {});\n        if (diffJSON.empty()) {\n            std::error_code ec;\n            std::filesystem::remove(path + \".diff.geojson\", ec);\n        } else {\n            // Dump the unexpected results to files for review\n            writeFile(path + \".new.geojson\", dump(actualJSON));\n            writeFile(path + \".diff.geojson\", dump(diffJSON));\n            std::cout << path << \":\" << diffJSON.size() << \" unexpected differences\\n\";\n        }\n    }\n#endif // MLT_WITH_JSON\n\n    return {std::move(tile), std::string()};\n}\n\n} // namespace\n\nTEST(Decode, SimplePointBoolean) {\n    const auto tile = loadTile(basePath / \"simple/point-boolean.mlt\").first;\n    ASSERT_TRUE(tile);\n\n    const auto* mltLayer = tile->getLayer(\"layer\");\n    EXPECT_TRUE(mltLayer);\n    EXPECT_EQ(mltLayer->getName(), \"layer\");\n    EXPECT_EQ(mltLayer->getExtent(), 4096);\n    EXPECT_EQ(mltLayer->getFeatures().size(), 1);\n\n    const auto& feature = mltLayer->getFeatures().front();\n    EXPECT_EQ(feature.getID(), 1);\n}\n\nTEST(Decode, SimpleLineBoolean) {\n    const auto tile = loadTile(basePath / \"simple/line-boolean.mlt\");\n    // TODO: check properties, geometry, etc.\n    ASSERT_TRUE(tile.first);\n}\n\nTEST(Decode, SimplePolygonBoolean) {\n    const auto tile = loadTile(basePath / \"simple/polygon-boolean.mlt\");\n    ASSERT_TRUE(tile.first);\n\n    const auto* layer = tile.first->getLayer(\"layer\");\n    ASSERT_TRUE(layer);\n    ASSERT_EQ(layer->getFeatures().size(), 1);\n\n    const auto& geom = layer->getFeatures()[0].getGeometry();\n    EXPECT_EQ(geom.type, mlt::metadata::tileset::GeometryType::POLYGON);\n    const auto& poly = dynamic_cast<const mlt::geometry::Polygon&>(geom);\n    ASSERT_EQ(poly.getRings().size(), 1);\n    // MLT stores rings without closing points; decoder must add them back\n    const auto& ring = poly.getRings()[0];\n    EXPECT_EQ(ring.front(), ring.back()) << \"polygon ring should be closed by decoder\";\n}\n\nTEST(Decode, SimpleMultiPointBoolean) {\n    const auto tile = loadTile(basePath / \"simple/multipoint-boolean.mlt\");\n    ASSERT_TRUE(tile.first);\n}\n\nTEST(Decode, SimpleMultiLineBoolean) {\n    const auto tile = loadTile(basePath / \"simple/multiline-boolean.mlt\");\n    ASSERT_TRUE(tile.first);\n}\n\nTEST(Decode, SimpleMultiPolygonBoolean) {\n    const auto tile = loadTile(basePath / \"simple/multipolygon-boolean.mlt\");\n    ASSERT_TRUE(tile.first);\n}\n\nTEST(Decode, Bing) {\n    const auto tile = loadTile(basePath / \"bing/4-13-6.mlt\");\n    ASSERT_TRUE(tile.first) << tile.second;\n}\n\nTEST(Decode, OMT) {\n    const auto tile = loadTile(basePath / \"omt/2_2_2.mlt\");\n    ASSERT_TRUE(tile.first);\n}\n\n// Make sure everything else loads without errors\nTEST(Decode, AllBing) {\n    const std::regex metadataFilePattern{\".*\\\\.mlt\"};\n    for (const auto& path : findFiles(basePath / \"bing\", metadataFilePattern)) {\n        std::cout << \"  Loading \" << path.filename().string() << \" ...\\n\";\n        loadTile(path);\n    }\n}\nTEST(Decode, AllAmazon) {\n    const std::regex metadataFilePattern{\".*\\\\.mlt\"};\n    for (const auto& path : findFiles(basePath / \"amazon\", metadataFilePattern)) {\n        std::cout << \"  Loading \" << path.filename().string() << \" ...\\n\";\n        loadTile(path);\n    }\n}\nTEST(Decode, AllOMT) {\n    const std::regex metadataFilePattern{\".*\\\\.mlt\"};\n    for (const auto& path : findFiles(basePath / \"omt\", metadataFilePattern)) {\n        try {\n            if (auto result = loadTile(path); result.first) {\n                std::cout << \"Loaded: \" << path.filename().string() << \"\\n\";\n            } else {\n                std::cout << \"Not Loaded: \" << path.filename().string() << \": \" << result.second << \"\\n\";\n            }\n        } catch (const std::exception& e) {\n            FAIL() << path.filename().string() << \" Failed: \" << e.what();\n        }\n    }\n}\n"
  },
  {
    "path": "cpp/test/test_fastpfor.cpp",
    "content": "#if !MLT_WITH_FASTPFOR\n#error This file should be excluded when FastPFor support is not enabled\n#endif\n\n#include <gtest/gtest.h>\n\n// From fastpfor/...\n#include <compositecodec.h>\n#include <fastpfor.h>\n#include <variablebyte.h>\n\n#if MLT_WITH_FASTPFOR_SIMD\n#include <simdfastpfor.h>\n#endif // MLT_WITH_FASTPFOR_SIMD\n\n#include <algorithm>\n#include <cstdint>\n#include <ranges>\n#include <string>\n#include <vector>\n\n// Test less than one block of data, which does not use both parts of the CompositeCodec\nTEST(FastPfor, DecompressPartial) {\n    const std::vector<std::uint32_t> data = {0, 9605520};\n    const std::vector<std::uint32_t> expected = {16, 17, 18};\n\n    FastPForLib::CompositeCodec<FastPForLib::FastPFor<8>, FastPForLib::VariableByte> codec;\n    const auto result = codec.uncompress(data, expected.size());\n    EXPECT_TRUE(std::ranges::equal(result, expected));\n}\n\nnamespace {\n// Return a sequence of 1,2,1,2,... of length n\nauto simpleValueView(std::size_t n) {\n    return std::views::iota(0ul) | std::views::transform([](auto x) { return (x & 1u) + 1u; }) | std::views::take(n) |\n           std::views::common;\n}\nauto simpleValues(std::size_t n) {\n    const auto view = simpleValueView(n);\n    return std::vector<uint32_t>(view.begin(), view.end());\n}\n} // namespace\n\n// Check decode of data encoded by the Java implementation with a payload larger than,\n// but not a multiple of, the block size. The result was captured before the final\n// byte-swapping, so no additional swapping is needed as with a typical payload.\nTEST(FastPfor, JavaV1) {\n    const std::uint32_t javaEncodedByteSwapped[] = {\n        0x00000300ul, 0x00000031ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul, 0x99999999ul,\n        0x99999999ul, 0x99999999ul, 0x00000006ul, 0x00020002ul, 0x00000002ul, 0x00000000ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n        0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul, 0x82818281ul,\n    };\n\n    std::vector<std::uint32_t> decoded(1000);\n    std::size_t outSize = decoded.size();\n    FastPForLib::CompositeCodec<FastPForLib::FastPFor<8>, FastPForLib::VariableByte> codec;\n    codec.decodeArray(reinterpret_cast<const uint32_t*>(javaEncodedByteSwapped),\n                      sizeof(javaEncodedByteSwapped) / sizeof(uint32_t),\n                      decoded.data(),\n                      outSize);\n    EXPECT_EQ(outSize, decoded.size());\n    EXPECT_EQ(decoded, simpleValues(decoded.size()));\n}\n\n#if MLT_WITH_FASTPFOR_SIMD\nnamespace {\ntemplate <int BLOCK_SIZE>\nvoid testSIMDInterop(int valuesCount) {\n    FastPForLib::CompositeCodec<FastPForLib::FastPFor<BLOCK_SIZE>, FastPForLib::VariableByte> codec;\n    FastPForLib::CompositeCodec<FastPForLib::SIMDFastPFor<BLOCK_SIZE>, FastPForLib::VariableByte> simd_codec;\n\n    const auto values = simpleValues(valuesCount);\n    const auto encoded = codec.compress(values);\n    const auto simd_encoded = simd_codec.compress(values);\n\n    {\n        SCOPED_TRACE(\"Plain uncompress of plain compress\");\n        EXPECT_EQ(codec.uncompress(encoded, values.size()), values);\n    }\n    {\n        SCOPED_TRACE(\"SIMD uncompress of SIMD compress\");\n        EXPECT_EQ(simd_codec.uncompress(simd_encoded, values.size()), values);\n    }\n    {\n        SCOPED_TRACE(\"SIMD uncompress of plain compress\");\n        const auto decoded = simd_codec.uncompress(encoded, values.size());\n        if (valuesCount < 32 * BLOCK_SIZE) {\n            EXPECT_EQ(decoded, values);\n        } else {\n            EXPECT_NE(decoded, values);\n        }\n    }\n    {\n        SCOPED_TRACE(\"Plain uncompress of SIMD compress\");\n        const auto decoded = codec.uncompress(simd_encoded, values.size());\n        if (valuesCount < 32 * BLOCK_SIZE) {\n            EXPECT_EQ(decoded, values);\n        } else {\n            EXPECT_NE(decoded, values);\n        }\n    }\n}\n\nstruct SIMDInteropCase {\n    int values;\n    int blockSize;\n};\n\nstd::vector<SIMDInteropCase> simdInteropCases() {\n    constexpr int valueCounts[] = {96, 128, 160, 256, 384};\n    constexpr int blockSizes[] = {4, 8};\n\n    std::vector<SIMDInteropCase> cases;\n    cases.reserve(std::size(valueCounts) * std::size(blockSizes));\n\n    for (const int values : valueCounts) {\n        for (const int blockSize : blockSizes) {\n            cases.push_back({values, blockSize});\n        }\n    }\n\n    return cases;\n}\n\nstd::string simdInteropCaseName(const ::testing::TestParamInfo<SIMDInteropCase>& info) {\n    return \"Values_\" + std::to_string(info.param.values) + \"_BlockSize_\" + std::to_string(info.param.blockSize);\n}\n\nclass PFORSIMDInteropTest : public ::testing::TestWithParam<SIMDInteropCase> {};\n\nTEST_P(PFORSIMDInteropTest, Interop) {\n    const auto testCase = GetParam();\n    switch (testCase.blockSize) {\n        case 4:\n            testSIMDInterop<4>(testCase.values);\n            break;\n        case 8:\n            testSIMDInterop<8>(testCase.values);\n            break;\n        default:\n            FAIL() << \"Unsupported block size: \" << testCase.blockSize;\n    }\n}\n\nINSTANTIATE_TEST_SUITE_P(FastPfor, PFORSIMDInteropTest, ::testing::ValuesIn(simdInteropCases()), simdInteropCaseName);\n} // namespace\n#endif // MLT_WITH_FASTPFOR_SIMD\n"
  },
  {
    "path": "cpp/test/test_fsst.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <mlt/decode/string.hpp>\n\n#include <string>\n#include <vector>\n\nTEST(FSST, DecodeFromJava) {\n    const std::string expected = \"AAAAAAABBBAAACCdddddEEEEEEfffEEEEAAAAAddddCC\";\n    const std::vector<std::uint8_t> symbols = {65, 65, 69, 69, 100, 100, 65, 66, 67, 69, 100, 102};\n    const std::vector<std::uint32_t> symbolLengths = {2, 2, 2, 1, 1, 1, 1, 1, 1};\n    const std::vector<std::uint8_t> javaCompressed = {0, 0, 0, 3, 4, 4, 4, 0, 3, 5, 5, 2, 2, 7, 1,\n                                                      1, 1, 8, 8, 8, 1, 1, 0, 0, 3, 2, 2, 5, 5};\n\n    const auto decoded = mlt::decoder::StringDecoder::decodeFSST(\n        symbols, symbolLengths, javaCompressed, expected.size());\n\n    EXPECT_EQ(decoded.size(), expected.size());\n    EXPECT_EQ(0, memcmp(expected.c_str(), decoded.data(), expected.size()));\n\n    // also make sure buffer growth works\n    const auto decoded2 = mlt::decoder::StringDecoder::decodeFSST(symbols, symbolLengths, javaCompressed, 0);\n    EXPECT_EQ(decoded2.size(), expected.size());\n    EXPECT_EQ(0, memcmp(expected.c_str(), decoded2.data(), expected.size()));\n\n    const auto decoded3 = mlt::decoder::StringDecoder::decodeFSST(\n        symbols, symbolLengths, javaCompressed, expected.size() / 2);\n    EXPECT_EQ(decoded3.size(), expected.size());\n    EXPECT_EQ(0, memcmp(expected.c_str(), decoded3.data(), expected.size() / 2));\n}\n"
  },
  {
    "path": "cpp/test/test_packed_bitset.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <mlt/util/packed_bitset.hpp>\n\nusing namespace mlt;\n\nTEST(PackedBitset, TestBit) {\n    EXPECT_FALSE(testBit({}, 0));\n    EXPECT_FALSE(testBit({}, 1));\n    EXPECT_FALSE(testBit({}, 1000));\n\n    EXPECT_FALSE(testBit({0xf0}, 0));\n    EXPECT_TRUE(testBit({0xf0}, 7));\n    EXPECT_FALSE(testBit({0xf0}, 8));\n\n    EXPECT_TRUE(testBit({0xf0, 0x01}, 8));\n}\n\nTEST(PackedBitset, NextBit) {\n    for (const auto i : {0, 7, 8}) {\n        EXPECT_FALSE(nextSetBit({}, i));\n        EXPECT_FALSE(nextSetBit({0}, i));\n        EXPECT_FALSE(nextSetBit({0, 0}, i));\n    }\n\n    EXPECT_EQ(nextSetBit({0xaa}, 0), 1);\n    EXPECT_EQ(nextSetBit({0xaa}, 1), 1);\n    EXPECT_EQ(nextSetBit({0xaa}, 2), 3);\n    EXPECT_EQ(nextSetBit({0xaa}, 3), 3);\n    EXPECT_EQ(nextSetBit({0xaa}, 4), 5);\n    EXPECT_EQ(nextSetBit({0xaa}, 5), 5);\n    EXPECT_EQ(nextSetBit({0xaa}, 6), 7);\n    EXPECT_EQ(nextSetBit({0xaa}, 7), 7);\n    EXPECT_FALSE(nextSetBit({0xaa}, 8));\n    EXPECT_EQ(nextSetBit({0xaa, 0xaa}, 8), 9);\n    EXPECT_EQ(nextSetBit({0x01, 0xc0}, 1), 14);\n}\n\nTEST(PackedBitset, CountBits) {\n    EXPECT_EQ(countSetBits({}), 0);\n    EXPECT_EQ(countSetBits({0}), 0);\n    EXPECT_EQ(countSetBits({0, 0}), 0);\n\n    EXPECT_EQ(countSetBits({0x01}), 1);\n    EXPECT_EQ(countSetBits({0x02}), 1);\n    EXPECT_EQ(countSetBits({0x04}), 1);\n    EXPECT_EQ(countSetBits({0x08}), 1);\n    EXPECT_EQ(countSetBits({0x10}), 1);\n    EXPECT_EQ(countSetBits({0x20}), 1);\n    EXPECT_EQ(countSetBits({0x40}), 1);\n    EXPECT_EQ(countSetBits({0x80}), 1);\n    EXPECT_EQ(countSetBits({0x01, 0}), 1);\n\n    EXPECT_EQ(countSetBits({0, 0, 0, 4}), 1);\n\n    EXPECT_EQ(countSetBits({0xff}), 8);\n}\n"
  },
  {
    "path": "cpp/test/test_util.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <mlt/util/vectorized.hpp>\n\nTEST(Util, ComponentwiseDeltaVec2) {\n    // input, expected output\n    const std::vector<std::pair<std::vector<std::int32_t>, std::vector<std::int32_t>>> componentwiseDeltaVec2Cases{\n        {{10, 14, 3, 9}, {5, 7, 3, 2}},\n        {{6, 12, 10, 12, 24, 44}, {3, 6, 8, 12, 20, 34}},\n        {{0, 0, 8192, 0, 0, 8192, 8191, 0}, {0, 0, 4096, 0, 4096, 4096, 0, 4096}},\n        {{\n             1416, 520, 1888, 6448, 2927, 1136, 224, 47,   5920, 4671, 752,  351,  1999, 1423,\n             447,  671, 1184, 1792, 143,  351,  623, 320,  95,   1055, 976,  880,  1407, 1471,\n             3983, 336, 703,  80,   1680, 559,  15,  1120, 1279, 848,  1312, 1280, 1055, 528,\n             511,  976, 1072, 175,  1072, 1423, 976, 352,  463,  416,  2527, 2896, 2192, 1167,\n         },\n         {\n             708,  260,  1652, 3484, 188,  4052, 300,  4028, 3260, 1692, 3636, 1516, 2636, 804,\n             2412, 468,  3004, 1364, 2932, 1188, 2620, 1348, 2572, 820,  3060, 1260, 2356, 524,\n             364,  692,  12,   732,  852,  452,  844,  1012, 204,  1436, 860,  2076, 332,  2340,\n             76,   2828, 612,  2740, 1148, 2028, 1636, 2204, 1404, 2412, 140,  3860, 1236, 3276,\n         }},\n        {{\n             558, 7970, 72, 13, 3766, 6579, 100,  34,   8,  90,   134,  78,   92, 33, 76, 0,  28, 25, 84,  22,\n             52,  13,   12, 41, 80,   9,    50,   34,   44, 1,    2,    79,   50, 21, 28, 47, 30, 17, 4,   25,\n             58,  53,   90, 7,  48,   14,   96,   19,   18, 20,   118,  11,   46, 49, 12, 71, 16, 11, 711, 1277,\n             15,  32,   15, 8,  13,   34,   13,   4,    25, 26,   17,   2,    1,  8,  8,  12, 5,  8,  5,   1,\n             0,   16,   9,  2,  0,    14,   7,    4,    0,  6,    5,    6,    0,  4,  9,  1,  9,  10, 14,  0,\n             0,   4,    7,  6,  9,    3,    3,    8,    17, 11,   1,    4,    11, 3,  1,  10, 19, 12, 3,   3,\n             2,   3,    15, 3,  0,    8,    19,   2,    25, 32,   18,   8,    0,  8,  37, 3,  9,  8,  5,   18,\n             19,  0,    5,  6,  3,    18,   13,   0,    0,  20,   7,    0,    7,  9,  13, 6,  11, 7,  9,   2,\n             0,   12,   7,  6,  17,   2,    45,   28,   19, 26,   10,   14,   7,  4,  11, 5,  19, 8,  13,  11,\n             11,  20,   23, 20, 13,   4,    13,   32,   37, 16,   17,   24,   23, 18, 5,  12, 33, 26, 13,  20,\n             4,   36,   49, 38, 17,   46,   1390, 1536, 15, 1679, 2447, 7360,\n         },\n         {\n             279,  3985, 315,  3978, 2198, 688, 2248, 705, 2252, 750,  2319, 789, 2365, 772,  2403, 772, 2417, 759,\n             2459, 770,  2485, 763,  2491, 742, 2531, 737, 2556, 754,  2578, 753, 2579, 713,  2604, 702, 2618, 678,\n             2633, 669,  2635, 656,  2664, 629, 2709, 625, 2733, 632,  2781, 622, 2790, 632,  2849, 626, 2872, 601,\n             2878, 565,  2886, 559,  2530, -80, 2522, -64, 2514, -60,  2507, -43, 2500, -41,  2487, -28, 2478, -27,\n             2477, -23,  2481, -17,  2478, -13, 2475, -14, 2475, -6,   2470, -5,  2470, 2,    2466, 4,   2466, 7,\n             2463, 10,   2463, 12,   2458, 11,  2453, 16,  2460, 16,   2460, 18,  2456, 21,   2451, 19,  2449, 23,\n             2440, 17,   2439, 19,   2433, 17,  2432, 22,  2422, 28,   2420, 26,  2421, 24,   2413, 22,  2413, 26,\n             2403, 27,   2390, 43,   2399, 47,  2399, 51,  2380, 49,   2375, 53,  2372, 62,   2362, 62,  2359, 65,\n             2357, 74,   2350, 74,   2350, 84,  2346, 84,  2342, 79,   2335, 82,  2329, 78,   2324, 79,  2324, 85,\n             2320, 88,   2311, 89,   2288, 103, 2278, 116, 2283, 123,  2279, 125, 2273, 122,  2263, 126, 2256, 120,\n             2250, 130,  2238, 140,  2231, 142, 2224, 158, 2205, 166,  2196, 178, 2184, 187,  2181, 193, 2164, 206,\n             2157, 216,  2159, 234,  2134, 253, 2125, 276, 2820, 1044, 2812, 204, 1588, 3884,\n         }}};\n\n    using namespace mlt::util::decoding::vectorized;\n    for (std::size_t i = 0; i < componentwiseDeltaVec2Cases.size(); ++i) {\n        const auto& [input, expected] = componentwiseDeltaVec2Cases[i];\n        auto output = input;\n        decodeComponentwiseDeltaVec2(output.data(), output.size());\n        EXPECT_EQ(output, expected);\n    }\n}\n"
  },
  {
    "path": "cpp/test/test_varint.cpp",
    "content": "#include <gtest/gtest.h>\n\n#include <mlt/util/buffer_stream.hpp>\n#include <mlt/util/varint.hpp>\n#include <mlt/util/vectorized.hpp>\n\nusing namespace mlt;\n\nnamespace {\ntemplate <typename T = std::uint32_t>\nT decode(std::vector<std::uint8_t> data) {\n    mlt::BufferStream buffer{std::string_view{reinterpret_cast<const char*>(data.data()), data.size()}};\n    return mlt::util::decoding::decodeVarint<T>(buffer);\n}\n} // namespace\n\nTEST(Varint, Size) {\n    using namespace mlt::util::decoding;\n    EXPECT_EQ(getVarintSize(0u), 1);\n    EXPECT_EQ(getVarintSize(1u), 1);\n    EXPECT_EQ(getVarintSize(0x7fu), 1);\n    EXPECT_EQ(getVarintSize(0x80u), 2);\n    EXPECT_EQ(getVarintSize(0x3fffu), 2);\n    EXPECT_EQ(getVarintSize(0x4000u), 3);\n    EXPECT_EQ(getVarintSize(0xffffffffu), 5);\n    EXPECT_EQ(getVarintSize(0x800000000ull), 6);\n}\n\nTEST(Varint, Decode) {\n    EXPECT_EQ(decode({0x0}), 0x0);\n    EXPECT_EQ(decode({0x7f}), 0x7f);\n    EXPECT_EQ(decode({0x80, 1}), 0x80);\n    EXPECT_EQ(decode({0xff, 0x7f}), 0x3fff);\n    EXPECT_THROW(decode({0xff, 0x80}), std::runtime_error);\n    EXPECT_EQ(decode({0x80, 0x80, 0x01}), 0x4000);\n    EXPECT_EQ(decode({0xff, 0xff, 0x03}), 0xffff);\n    EXPECT_EQ(decode({0xff, 0xff, 0xff, 0xff, 0x0f}), 0xffffffff);\n\n    EXPECT_EQ(decode<std::uint64_t>({0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}),\n              0xffffffffffffffffULL);\n    EXPECT_THROW(decode<std::uint64_t>({0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02}),\n                 std::runtime_error);\n}\n"
  },
  {
    "path": "cpp/tool/CMakeLists.txt",
    "content": "add_executable(mlt-cpp-json mlt-json.cpp synthetic-geojson.cpp)\nset_property(TARGET mlt-cpp-json PROPERTY CXX_STANDARD 20)\nset(MLT_WITH_JSON ON CACHE BOOL \"Include JSON Support\" FORCE)\ntarget_link_libraries(mlt-cpp-json PRIVATE mlt-cpp)\n"
  },
  {
    "path": "cpp/tool/mlt-json.cpp",
    "content": "#include <mlt/decoder.hpp>\n#include <mlt/metadata/tileset.hpp>\n\n#include <cstring>\n#include <iostream>\n#include <fstream>\n#include <string>\n\nusing namespace std::string_literals;\nusing namespace std::string_view_literals;\n\n#include <mlt/json.hpp>\n#include \"synthetic-geojson.hpp\"\n\nnamespace {\n\nstd::vector<std::ifstream::char_type> loadFile(const std::string& path) {\n    std::ifstream file(path, std::ios::binary | std::ios::ate);\n    if (file.is_open()) {\n        const std::size_t size = file.tellg();\n        file.seekg(0);\n\n        std::vector<std::ifstream::char_type> buffer(size);\n        if (file.read(buffer.data(), size)) {\n            return buffer;\n        }\n    }\n    return {};\n}\n\n} // namespace\n\nint main(int argc, char** argv) {\n    if ((argc != 2 && argc != 5) || !std::strcmp(argv[1], \"--help\")) {\n        std::cerr << \"Decode a MapLibre Vector Tile and output it as JSON or GeoJSON.\\n\"\n                  << \"GeoJSON is used if tile coordinates are provided.\\n\\n\"\n                  << \"Usage: \" << argv[0] << \" <tile file> [<z> <x> <y>]\\n\";\n        return 1;\n    }\n\n    std::uint32_t x = 0;\n    std::uint32_t y = 0;\n    std::uint32_t z = 0;\n    const bool geoJSON = (argc == 5);\n    if (geoJSON) {\n        z = static_cast<std::uint32_t>(std::stoul(argv[2]));\n        x = static_cast<std::uint32_t>(std::stoul(argv[3]));\n        y = static_cast<std::uint32_t>(std::stoul(argv[4]));\n    }\n\n    const std::string baseName = argv[1];\n    auto buffer = loadFile(baseName);\n    if (buffer.empty()) {\n        std::cerr << \"Failed to load \" + baseName + \"\\n\";\n        return 1;\n    }\n\n    mlt::Decoder decoder(/*supportFastPFOR=*/true);\n    const auto tileData = decoder.decode({buffer.data(), buffer.size()});\n\n    nlohmann::json tileJSON;\n    if (geoJSON) {\n        tileJSON = mlt::json::toJSON(tileData, {.x = x, .y = y, .z = z}, true);\n    } else {\n        tileJSON = mlt::test::toFeatureCollection(tileData);\n    }\n    std::cout << tileJSON.dump(2, ' ', false, nlohmann::json::error_handler_t::replace);\n\n    return 0;\n}\n"
  },
  {
    "path": "cpp/tool/synthetic-geojson.cpp",
    "content": "#include \"synthetic-geojson.hpp\"\n\n#include <mlt/coordinate.hpp>\n#include <mlt/feature.hpp>\n#include <mlt/geometry.hpp>\n#include <mlt/json.hpp>\n#include <mlt/layer.hpp>\n#include <mlt/metadata/tileset.hpp>\n\nnamespace mlt::test {\n\nusing json = nlohmann::json;\n\nnamespace {\n\n/// Serialize a coordinate value as an integer if it is a whole number, otherwise as a float\njson buildRawCoordValue(float v) {\n    auto i = static_cast<std::int64_t>(v);\n    return (v == static_cast<float>(i)) ? json(i) : json(v);\n}\n\njson buildRawCoordinateArray(const Coordinate& coord) {\n    return json::array({buildRawCoordValue(coord.x), buildRawCoordValue(coord.y)});\n}\n\njson buildRawCoordinatesArray(const CoordVec& coords) {\n    return mlt::json::detail::buildArray(coords, [](const auto& c) { return buildRawCoordinateArray(c); });\n}\n\njson buildRawPolygonCoords(const std::vector<CoordVec>& rings) {\n    return mlt::json::detail::buildArray(rings, [](const auto& ring) { return buildRawCoordinatesArray(ring); });\n}\n\njson buildRawGeometryElement(const geometry::Geometry& geometry) {\n    using metadata::tileset::GeometryType;\n    switch (geometry.type) {\n        case GeometryType::POINT: {\n            const auto& g = static_cast<const geometry::Point&>(geometry);\n            return {{\"type\", \"Point\"}, {\"coordinates\", buildRawCoordinateArray(g.getCoordinate())}};\n        }\n        case GeometryType::LINESTRING: {\n            const auto& g = static_cast<const geometry::LineString&>(geometry);\n            return {{\"type\", \"LineString\"}, {\"coordinates\", buildRawCoordinatesArray(g.getCoordinates())}};\n        }\n        case GeometryType::POLYGON: {\n            const auto& g = static_cast<const geometry::Polygon&>(geometry);\n            return {{\"type\", \"Polygon\"}, {\"coordinates\", buildRawPolygonCoords(g.getRings())}};\n        }\n        case GeometryType::MULTIPOINT: {\n            const auto& g = static_cast<const geometry::MultiPoint&>(geometry);\n            return {{\"type\", \"MultiPoint\"}, {\"coordinates\", buildRawCoordinatesArray(g.getCoordinates())}};\n        }\n        case GeometryType::MULTILINESTRING: {\n            const auto& g = static_cast<const geometry::MultiLineString&>(geometry);\n            return {{\"type\", \"MultiLineString\"},\n                    {\"coordinates\", mlt::json::detail::buildArray(g.getLineStrings(), [](const auto& ls) {\n                         return buildRawCoordinatesArray(ls);\n                     })}};\n        }\n        case GeometryType::MULTIPOLYGON: {\n            const auto& g = static_cast<const geometry::MultiPolygon&>(geometry);\n            return {{\"type\", \"MultiPolygon\"},\n                    {\"coordinates\", mlt::json::detail::buildArray(g.getPolygons(), [](const auto& poly) {\n                         return buildRawPolygonCoords(poly);\n                     })}};\n        }\n        default:\n            throw std::runtime_error(\"Unsupported geometry type \" + std::to_string(std::to_underlying(geometry.type)));\n    }\n}\n\n} // anonymous namespace\n\njson toFeatureCollection(const MapLibreTile& tile) {\n    auto features = json::array();\n    for (const auto& layer : tile.getLayers()) {\n        for (const auto& feature : layer.getFeatures()) {\n            auto props = mlt::json::detail::buildProperties(layer, feature);\n            props[\"_extent\"] = layer.getExtent();\n            props[\"_layer\"] = layer.getName();\n\n            auto featureObj = json{\n                {\"geometry\", buildRawGeometryElement(feature.getGeometry())},\n                {\"properties\", std::move(props)},\n                {\"type\", \"Feature\"},\n            };\n            if (const auto id = feature.getID(); id.has_value()) {\n                featureObj[\"id\"] = *id;\n            }\n            features.push_back(std::move(featureObj));\n        }\n    }\n    return {{\"features\", std::move(features)}, {\"type\", \"FeatureCollection\"}};\n}\n\n} // namespace mlt::test\n"
  },
  {
    "path": "cpp/tool/synthetic-geojson.hpp",
    "content": "#pragma once\n\n#include <mlt/tile.hpp>\n#include <nlohmann/json.hpp>\n\n/// Test utility: convert an MLT tile to a flat GeoJSON FeatureCollection with raw tile-space\n/// coordinates. Layer name and extent are stored as `_layer` and `_extent` in each feature's\n/// properties. This matches the format used by test/synthetic expected JSON files.\nnamespace mlt::test {\n\nnlohmann::json toFeatureCollection(const MapLibreTile& tile);\n\n} // namespace mlt::test\n"
  },
  {
    "path": "cpp/tool/synthetic.test.ts",
    "content": "import { execFileSync } from \"node:child_process\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n  compareWithTolerance,\n  getTestCases,\n  writeActualOutput,\n} from \"synthetic-test-utils\";\nimport { describe, expect, it } from \"vitest\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst binary = resolve(__dirname, \"../build/tool/mlt-cpp-json\");\n\nconst SKIPPED_TESTS = [];\n\ndescribe(\"MLT Decoder - Synthetic tests\", () => {\n  expect.addEqualityTesters([compareWithTolerance]);\n  const testCases = getTestCases(SKIPPED_TESTS);\n  for (const { name, content, fileName } of testCases.active) {\n    it(name, async () => {\n      const actual = await decodeMLT(fileName);\n      try {\n        expect(actual).toEqual(content);\n      } catch (error) {\n        writeActualOutput(fileName, actual);\n        throw error;\n      }\n    });\n  }\n\n  for (const skippedTest of testCases.skipped) {\n    it.skip(skippedTest, () => {\n      // Test is skipped since it is not supported yet\n    });\n  }\n});\n\nasync function decodeMLT(mltFilePath: string) {\n  const output = execFileSync(binary, [mltFilePath], { encoding: \"utf-8\" });\n  return JSON.parse(output);\n}\n"
  },
  {
    "path": "cpp/vendor/fastpfor.cmake",
    "content": "if(NOT MLT_WITH_FASTPFOR)\n  message(STATUS \"[MLT] No FastPFOR support\")\n  return()\nendif(NOT MLT_WITH_FASTPFOR)\n\nmessage(STATUS \"[MLT] Including FastPFOR support\")\n\n# The fastpfor gtest targets conflict with ours\nset(FASTPFOR_WITH_TEST OFF CACHE BOOL \"Disable tests in FastPFor\" FORCE)\n\nadd_subdirectory(\"${PROJECT_SOURCE_DIR}/vendor/fastpfor\" \"${CMAKE_CURRENT_BINARY_DIR}/fastpfor\" EXCLUDE_FROM_ALL SYSTEM)\n\n# Disable all warnings for FastPFOR\nif(MSVC)\n    target_compile_options(FastPFOR PRIVATE /w)\nelse()\n    target_compile_options(FastPFOR PRIVATE -w)\nendif()\n\ntarget_link_libraries(mlt-cpp FastPFOR)\ntarget_include_directories(mlt-cpp PRIVATE SYSTEM \"${PROJECT_SOURCE_DIR}/vendor/fastpfor/headers\")\ntarget_compile_definitions(mlt-cpp PUBLIC MLT_WITH_FASTPFOR=1)\nif(MLT_WITH_FASTPFOR_SIMD)\n    target_compile_definitions(mlt-cpp PUBLIC MLT_WITH_FASTPFOR_SIMD=1)\nendif(MLT_WITH_FASTPFOR_SIMD)\nlist(APPEND MLT_EXPORT_TARGETS FastPFOR)\n"
  },
  {
    "path": "docs/assets/extra.css",
    "content": "[data-md-color-scheme=\"default\"] {\n  --md-primary-fg-color: #295daa;\n  --md-accent-fg-color: #568ad6;\n}\n\n[data-md-color-scheme=\"slate\"] {\n  --md-primary-fg-color: #295daa;\n  --md-accent-fg-color: #568ad6;\n}\n\n.md-nav__title {\n  display: none;\n}\n\n.md-nav--primary .md-nav__link[for=\"__toc\"] > .md-nav__icon,\n.md-nav--primary .md-nav__link[for=\"__toc\"] ~ .md-nav {\n  display: none;\n}\n\n.md-copyright__highlight {\n  color: inherit;\n}\n\n.inline-attribution {\n  font-size: smaller;\n}\n\n.experimental {\n  background-color: #ffeb3b;\n  color: #333;\n  padding: 2px 6px;\n  border-radius: 3px;\n  font-size: 0.8em;\n  font-weight: bold;\n  margin-left: 4px;\n}\n\n.experimental::before {\n  content: \"experimental\";\n}\n\n[data-md-color-scheme=\"slate\"] .experimental {\n  background-color: #ffc107;\n  color: #000;\n}\n"
  },
  {
    "path": "docs/assets/spec/mlt_tileset_metadata.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"version\": {\n      \"type\": \"integer\"\n    },\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"description\": {\n      \"type\": \"string\"\n    },\n    \"attribution\": {\n      \"type\": \"string\"\n    },\n    \"minZoom\": {\n      \"type\": \"integer\"\n    },\n    \"maxZoom\": {\n      \"type\": \"integer\"\n    },\n    \"bounds\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"left\": {\n            \"type\": \"number\"\n          },\n          \"top\": {\n            \"type\": \"number\"\n          },\n          \"right\": {\n            \"type\": \"number\"\n          },\n          \"bottom\": {\n            \"type\": \"number\"\n          }\n        },\n        \"required\": [\n          \"left\",\n          \"top\",\n          \"right\",\n          \"bottom\"\n        ]\n      }\n    },\n    \"center\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"latitude\": {\n            \"type\": \"number\"\n          },\n          \"longitude\": {\n            \"type\": \"number\"\n          }\n        },\n        \"required\": [\n          \"latitude\",\n          \"longitude\"\n        ]\n      }\n    }\n  },\n  \"required\": [\n    \"version\"\n  ]\n}\n"
  },
  {
    "path": "docs/assets/spec/place_feature.json",
    "content": "{\n  \"title\": \"Place Feature\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"id\": { \"type\": \"integer\" },\n    \"geometry\": { \"type\": \"MultiPolygon\" },\n\t \"properties\": {\n\t\t\"type\": \"object\",\n\t\t\"properties\": {\n\t\t\t\"speed\": { \"type\": \"number\" },\n\t\t\t\"rank\": { \"type\": \"integer\" },\n\t\t\t\"name\": {\n\t\t\t\t\"type\": \"object\",\n\t\t\t\t\"properties\": {\n\t\t\t\t\t\"en\": { \"type\": \"string\" },\n\t\t\t\t\t\"de\": { \"type\": \"string\" }\n\n\t\t\t\t},\n\t\t\t\t\"required\": [\"en\"]\n\t\t\t}\n\t\t}\n    }\n  },\n  \"required\": [\"geometry\"]\n}\n"
  },
  {
    "path": "docs/encodings.md",
    "content": "<h1>Encoding Definitions</h1>\n\n[TOC]\n\n---\n\n# Plain\n\nNo compression is applied to the data.\nDepending on the data type, values are stored in the following formats:\n\n- **Integer**: Little-Endian byte order\n- **Long**: Little-Endian byte order\n- **Float**: IEEE754 floating-point numbers in Little-Endian byte order\n- **Double**: IEEE754 floating-point numbers in Little-Endian byte order\n- **String**: Length and data streams\n\n# Boolean-RLE\n\nThis encoding compresses boolean columns using least-significant bit numbering (bit-endianness).\nRefer to the [ORC specification](https://orc.apache.org/specification/ORCv1/#boolean-run-length-encoding) for implementation details.\n\n# Byte-RLE\n\nThis encoding compresses byte streams, such as the `GeometryType` stream in Geometry columns.\nRefer to the [ORC specification](https://orc.apache.org/specification/ORCv1/#byte-run-length-encoding) for implementation details.\n\n# Dictionary Encoding\n\nDictionary encoding compactly represents repeated values and can be applied to `String` and `Geometry` columns.\nDistinct values are stored in a `dictionary` stream, while a separate `data` stream stores indices into the dictionary.\nWe allow the following encodings of this concept:\n\n## String Dictionary Encoding\n\nThe dictionary stream contains distinct UTF-8 encoded string values.\nThe data stream contains dictionary indices encoded as `UInt32`.\nA dictionary-encoded nullable string column consists of the following streams in order:\n`Present`, `Length`, `Dictionary`, `Data`\n\n!!! TODO\n    Is \"can\" (⇒ MAY) here the right terminology?\n    This implies that streams might be encoded by one of the options below, but also other options.\n    If yes, what is the alternative options?\n    If no, clarify this misunderstanding.\n\nAll streams can be further compressed using lightweight encodings:\n\n- **Present Stream**: Boolean-RLE\n- **Length and Data Stream**: See Integer encodings\n- **Dictionary**: See FSST Dictionary\n\n### FSST Dictionary Encoding\n\nDictionary encoding requires fully repeating strings to reduce size. However, geospatial attributes often contain strings with common prefixes that are not identical (e.g., localized country names).\nFSST replaces frequently occurring substrings while supporting efficient scans and random lookups. It further compresses UTF-8 encoded dictionary values in MLT.\nAn FSST dictionary-encoded nullable string column consists of the following streams in order:\n`Present`, `SymbolLength`, `SymbolTable`, `String Length`, `Dictionary` (Compressed Corpus)\nFor implementation details, refer to [this paper](https://www.vldb.org/pvldb/vol13/p2649-boncz.pdf).\n\n**Available implementations**:\n\n- **C++**: <https://github.com/cwida/fsst>\n- **Java**: Work in progress\n- **JS/WebAssembly decoder**: Work in progress (simple to implement)\n\n### Shared Dictionary Encoding\n\nShared dictionary encoding allows multiple columns to share a common dictionary.\nNested fields can also use a shared dictionary (e.g., for localized values like `name:*` columns in an OSM dataset).\nIf used for nested fields, all fields sharing the dictionary must be grouped together in the file and prefixed with the dictionary.\n\nA shared dictionary-encoded nullable string column consists of the following streams in order:\n`Length`, `Dictionary`, `Present1`, `Data1`, `Present2`, `Data2`\n\nA shared FSST dictionary-encoded nullable string column consists of the following streams in order:\n`SymbolLength`, `SymbolTable`, `String Length`, `Dictionary` (Compressed Corpus), `Present1`, `Data1`, `Present2`, `Data2`\n\n## Vertex Dictionary Encoding\n\nUses an additional `VertexOffsets` stream to store indices for vertex coordinates in the `VertexBuffer` stream.\nVertices in the VertexBuffer are sorted using a Hilbert curve and delta-encoded with null suppression.\n\n### Morton Vertex Dictionary Encoding\n\n`VertexBuffer` coordinates are transformed into a 1D integer using Morton code.\nThe data is sorted by Morton code and further compressed using integer compression techniques.\n\n# Integer Encodings\n\nMost data in MLT is stored as integer arrays, making efficient integer compression crucial.\n\n## Logical-Level Techniques\n\nIntegers are encoded using delta encoding, run-length encoding (RLE), or a combination of both (delta-RLE).\n\n### Delta Encoding\n\nComputes differences between consecutive elements (e.g., x₂ - x₁, x₃ - x₂, ...).\nUsed with [physical-level techniques](#physical-level-techniques) to reduce the number of bits required for storing delta values.\n\n### Run-Length Encoding (RLE)\n\nRefer to [Wikipedia](https://en.wikipedia.org/wiki/Run-length_encoding) for a basic explanation.\nRuns and values are stored in separate buffers.\nFor unsigned integers, ZigZag encoding is applied to the values buffer.\nBoth buffers are further compressed using null suppression.\n\n### Delta-RLE\n\nApplies delta encoding followed by RLE.\nEfficient for ascending sequences like `id` fields.\n\n## Physical-Level Techniques\nNull suppression techniques are used to compress integer arrays by reducing the number of bits required to store each integer.\n\n### ZigZag Encoding\n\nUsed for encoding unsigned integers in null suppression techniques.\n[ZigZag encoding](https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding) uses the least significant bit to represent the sign.\n\n### VarInt Encoding\n\nA byte-aligned null suppression technique that compresses integers using a minimal number of bytes.\nFor implementation details, refer to [Protobuf](https://protobuf.dev/programming-guides/encoding/#varints).\n\n### SIMD-FastPFOR\n\nA bit-aligned null suppression technique that compresses integers using a minimal number of bits.\nUses a patched approach to store exceptions (outliers) separately, keeping the overall bit width small.\n\nRefer to:\n- <https://arxiv.org/pdf/1209.2137.pdf>\n- <https://ayende.com/blog/199524-C/integer-compression-the-fastpfor-code>\n\n**Available implementations**:\n\n- **C++**: <https://github.com/fast-pack/FastPFOR>\n- **Java**: <https://github.com/fast-pack/JavaFastPFOR>\n- **C#**: <https://github.com/Genbox/CSharpFastPFOR>\n- **JS/WebAssembly**: Work in progress (higher implementation complexity)\n"
  },
  {
    "path": "docs/implementation-status.md",
    "content": "# Implementation Status\n\n## Integrations\n\n| Integration | Status |\n|---|---|\n| [MapLibre Native](https://maplibre.org/maplibre-native/) | Supported since MapLibre Android 12.1.0, MapLibre iOS 6.2.0. PR: [#3246](https://github.com/maplibre/maplibre-native/pull/3246). |\n| [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/) | Supported since version 5.12.0. PR: [#6570](https://github.com/maplibre/maplibre-gl-js/pull/6570). |\n| Planetiler | Can generate tiles in MLT since version [0.10.0](https://github.com/onthegomap/planetiler/releases/tag/v0.10.0). |\n| PMTiles | Can store MLT, see [PMTiles v3 spec - Tile Type](https://github.com/protomaps/PMTiles/blob/main/spec/v3/spec.md#tile-type-tt). |\n| [Martin tile server](https://maplibre.org/martin/) | Can detect and serve MLT since version [1.3.0](https://github.com/maplibre/martin/releases/tag/martin-v1.3.0). PR: [#2512](https://github.com/maplibre/martin/pull/2512). |\n\n## Implementations\n\n| Implementation | Language | Type | Notes |\n|---|---|---|---|\n| [maplibre-tile-spec/cpp](https://github.com/maplibre/maplibre-tile-spec/tree/main/cpp) | C++ | Decoder | Used by MapLibre Native |\n| [maplibre-tile-spec/js](https://github.com/maplibre/maplibre-tile-spec/tree/main/js) | TypeScript | Decoder | Used by MapLibre GL JS |\n| [maplibre-tile-spec/java](https://github.com/maplibre/maplibre-tile-spec/tree/main/java) | Java | Encoder | |\n| [maplibre-tile-spec/rust/mlt](https://github.com/maplibre/maplibre-tile-spec/tree/main/rust/mlt) | Rust | Decoder / Encoder | |\n"
  },
  {
    "path": "docs/index.md",
    "content": "# Introduction\n\n--8<-- \"live-spec-note\"\n\nMLT is natively supported by [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/) and [MapLibre Native](https://maplibre.org/maplibre-native/), and tiles can be served using the [Martin tile server](https://maplibre.org/martin/).\n\nThe MLT format is mainly inspired by the [MVT format](https://github.com/mapbox/vector-tile-spec), but has been redesigned from the ground up to improve the following areas:\n\n- **Improved compression ratio** - up to 6x on large tiles, based on a column oriented layout with (custom) lightweight encodings\n- **Better decoding performance** - fast lightweight encodings which can be used in combination with SIMD/vectorization instructions\n- **Support for linear referencing and m-values** to efficiently support the upcoming next generation source formats such as Overture Maps (GeoParquet)\n- **Support 3D coordinates**, i.e. elevation\n- **Support complex types**, including nested properties, lists and maps\n- **Improved processing performance**: Based on an in-memory format that can be processed efficiently on the CPU and GPU and loaded directly into GPU buffers partially (like polygons in WebGL) or completely (in case of WebGPU compute shader usage) without additional processing\n"
  },
  {
    "path": "docs/snippets/live-spec-note",
    "content": "!!! NOTE\n    This is a live specification that evolves continuously. Features marked as <span class=\"experimental\"></span> are under active development and may change in future versions. Stable features are those without experimental tags.\n"
  },
  {
    "path": "docs/specification.md",
    "content": "<h1>MapLibre Tile Specification</h1>\n\n--8<-- \"live-spec-note\"\n\n[TOC]\n\n---\n\n# Basics\n\nAn MLT (MapLibre Tile) contains information about a specific geographic region, known as a tile.\nEach tile is a collection of `FeatureTables`, which are equivalent to `Layers` in the [MVT specification](https://github.com/mapbox/vector-tile-spec).\n\nA `FeatureTable` contains thematically grouped vector data, known as `Features`.\nA `FeatureTable` can contain up to `2^31 - 1` (2,147,483,647) features; this limit is chosen so the feature count fits in signed 32-bit integer fields used by decoders.\n\nFeatures within a single FeatureTable share a common set of attribute columns (properties) and typically share the same geometry type (though this is not strictly required).\n\nEach `FeatureTable` is preceded by a `FeatureTableMetadata` that describes `FeatureTable`'s structure.\n\nThe visual appearance of a tile is usually defined by a [MapLibre Style](https://maplibre.org/maplibre-style-spec/), which specifies how features are rendered.\n\nEach feature must have\n- a `geometry` column (type based on the OGC's Simple Feature Access Model (SFA), excluding support for `GeometryCollection` types)\n- an optional `id` column\n- optional property columns\n\nWhile geometries are not restricted to a single type, using one type per table is recommended for efficiency.\nAs in MVT, geometry coordinates are encoded as integers within vector tile grid coordinates.\n\n!!! NOTE\n    The terms `column`, `field`, and `property` are used interchangeably in this document.\n\n# Tile Layout\n\nA `FeatureTable` in the MLT specification uses a tabular, column-oriented layout.\nIt employs various lightweight compression schemes to encode column values efficiently.\n\nA FeatureTable consists of a mandatory `geometry` column, an optional `id` column, and optional property columns.\nThe absence of a single header at the beginning of the tile allows `FeatureTable`s to be constructed independently, and even concatenated on the fly.\n\nA logical column is separated into several physical `streams` (sub-columns), inspired by the ORC file format.\nThese streams are stored contiguously.\nA stream is a sequence of values of a known length in a continuous memory chunk, all sharing the same type.\nStreams include additional metadata, such as their size and encoding type.\n\nFor example, a nullable string property column might have:\n- A **`present` stream** (a bit flag indicating the presence of a value).\n- A **`length` stream** (describing the number of characters for each string).\n- A **`data` stream** (containing the actual UTF-8 encoded string values).\n\nMLT defines the following stream types:\n\n- **Present**:\n  Enables efficient encoding of sparse columns by indicating value presence via a bit flag.\n  This stream can be omitted if the column is not nullable (as declared in the `FieldMetadata`).\n- **Data**:\n  Stores the actual column data (e.g., `boolean`, `int`, `float`, or `string` values for feature properties, dictionary-encoded values, or geometry coordinates).\n  For fixed-size data types (`boolean`, `int`, `float`), this is the only required stream besides the optional `present` stream.\n- **Length**:\n  Specifies the number of elements for variable-sized data types like strings or lists.\n- **Offset**:\n  Stores offsets into a data stream when using dictionary encoding (e.g., for strings or vertices).\n\nThese physical streams are further categorized into logical streams that define how to interpret the data:\n\n```mermaid\nclassDiagram\n    class DictionaryLogicalStreamType {\n        <<enumeration>>\n        Single\n        Shared\n        Vertex\n        Morton\n        FSST\n    }\n\n    class OffsetLogicalStreamType {\n        <<enumeration>>\n        Vertex\n        Index\n        String\n        Key\n    }\n\n    class LengthLogicalStreamType {\n        <<enumeration>>\n        Geometries\n        Parts\n        Rings\n        Triangles\n        Symbol\n        Dictionary\n    }\n```\n\n## Metadata\n\n### Tileset Metadata <span class=\"experimental\"></span>\n\n!!! NOTE\n    Tileset metadata was initially implemented as a size reduction experiment.\n    This feature is not currently supported.\n\nGlobal metadata for the entire tileset is stored separately in a [JSON file](assets/spec/mlt_tileset_metadata.json).\n\nThis tileset metadata provides information for the full tileset and is the equivalent of the TileJSON spec commonly used with MVT and other tile types.\nBy defining this information once per tileset, we avoid redundant metadata in each tile, saving significant space, especially for small tiles.\n\n### Tile Metadata\nThere is no global tile header.  Each `FeatureTable` has its own metadata.\n\n### FeatureTable Metadata\n\nEach `FeatureTable` is preceded by a `FeatureTableMetadata` section describing it.\n\n!!! CAUTION\n    This is not clear, and possibly incorrect.\n    Why any number?\n    Should the size of the upcoming metadata and table be part of that structure?\n\nA FeatureTable consists of any number of the following sequences:\n- The size of the upcoming `FeatureTableMetadata` (varint-encoded).\n- The size of the upcoming `FeatureTable` (varint-encoded).\n- One `FeatureTableMetadata` section.\n- One `FeatureTable` section.\n\nThis structure allows a tile to be built by simply concatenating separate results.\nThe `FeatureTableMetadata` is described in detail below.\n\nWithin a `FeatureTable`, additional metadata describes the structure of each part:\n\n- **FieldMetadata**:\n  Contains information about a field (column), including the number of streams it comprises and its vector type for efficient decoding into the in-memory format.\n  Every field section is preceded by a `FieldMetadata` section.\n- **StreamMetadata**:\n  Contains information about a stream, such as the encoding scheme used and the number of values.\n  Every stream section is preceded by a `StreamMetadata` section.\n\nSince every `Field` has a `FieldMetadata` section, even for fields absent in a specific tile, no `id` is needed.\nA field's absence is indicated by a zero value for its number of streams.\nAll integers in metadata sections are `Varint`-encoded (for u32) or bit-packed (for u8).\n\n```mermaid\n---\nconfig:\n  class:\n    hideEmptyMembersBox: true\ntitle: FeatureTableSchema\n---\nclassDiagram\ndirection TB\n    class Tile {\n      +LayerGroup[] groups\n    }\n\n    class LayerGroup {\n      +VarInt metadataSize\n      +TileMetadata metadata\n      +u8[] tileData\n    }\n\n    class TileMetadata {\n      +FeatureTable[] featureTables\n    }\n\n    class FeatureTable {\n      +String name\n      +VarInt columnCount\n      +Column[] columns\n    }\n\n    class Column {\n      +ColumnOptions options %% VarInt\n      +String name\n      +ScalarColumn scalarType %% oneof i.e., scalarType XOR complexType\n      +ComplexColumn complexType\n    }\n\n    Tile --> LayerGroup : groups\n    LayerGroup --> TileMetadata : metadata\n    TileMetadata --> FeatureTable : featureTables\n    FeatureTable --> Column : columns\n```\n\nStrings are encoded as UTF-8 sequences of characters with a length header:\n\n```mermaid\n---\nconfig:\n  class:\n    hideEmptyMembersBox: true\ntitle: StringsSchema\n---\nclassDiagram\ndirection TB\n    class String {\n      +VarInt length\n      +u8 bytes[length] %% encoding is always UTF-8\n    }\n```\n\nColumns can be thought of as a union of scalar and complex columns and can have these options:\n\n```mermaid\n---\nconfig:\n  class:\n    hideEmptyMembersBox: true\ntitle: ColumnsSchema\n---\nclassDiagram\ndirection TB\n    class FieldOptions {\n      NULLABLE = 1, %% Property is nullable\n      COMPLEX_TYPE = 2, %% A complexType follows if set, else a scalarType [EXPERIMENTAL]\n      LOGICAL_TYPE = 4, %% A logical type follows if set, else a physical type [EXPERIMENTAL]\n      CHILD_TYPES = 8, %% 1: Child types are present [EXPERIMENTAL]\n    }\n\n    class ColumnOptions {\n      VERTEX_SCOPE = 16, %% Property is vertex-scope if set, else feature-scope\n    }\n\n    <<enumeration>> FieldOptions\n    <<enumeration>> ColumnOptions\n\n    FieldOptions <|-- ColumnOptions\n```\n\nScalar columns are laid out as follows:\n\n```mermaid\n---\nconfig:\n  class:\n    hideEmptyMembersBox: true\ntitle: ScalarColumnsSchema\n---\nclassDiagram\ndirection TB\n    class Column {\n    }\n\n    class ScalarColumn {\n      +ScalarColumnOptions options %% VarInt\n      +ScalarType physicalType %% oneof i.e., physicalType XOR logicalType\n      +LogicalScalarType logicalType\n    }\n\n    class ScalarType {\n      BOOLEAN = 0\n      INT_8 = 1\n      UINT_8 = 2\n      INT_32 = 3\n      UINT_32 = 4\n      INT_64 = 5\n      UINT_64 = 6\n      FLOAT = 7\n      DOUBLE = 8\n      STRING = 9\n      INT_128 = 10\n      UINT_128 = 11\n    }\n\n    class LogicalScalarType {\n      TIMESTAMP = 0\n      DATE = 1\n      JSON = 2\n    }\n\n    <<enumeration>> ScalarType\n    <<enumeration>> LogicalScalarType\n\n    note for LogicalScalarType \"[EXPERIMENTAL]\"\n\n    Column --> ScalarColumn : scalarType\n    ScalarColumn --> ScalarType : physicalType\n    ScalarColumn --> LogicalScalarType : logicalType\n```\n\nAnd `ComplexColumns` like this:\n\n```mermaid\n---\ntitle: ComplexColumnsSchema\nconfig:\n  class:\n    hideEmptyMembersBox: true\n---\nclassDiagram\ndirection TB\n    class ComplexColumn {\n      +ComplexType physicalType %% oneof i.e., physicalType XOR logicalType\n      +LogicalComplexType logicalType\n      +VarInt childCount %% Present only if CHILD_TYPES is set in columnOptions\n      +Field[] children\n    }\n\n    class Field {\n      +FieldOptions options %% VarInt\n      +String name\n      +ScalarField scalarField %% oneof i.e., scalarField XOR complexField\n      +ComplexField complexField\n    }\n\n    class ScalarField {\n      +ScalarType physicalType %% oneof i.e., physicalType XOR logicalType\n      +LogicalScalarType logicalType\n    }\n\n    class ComplexField {\n      +ComplexType physicalType %% oneof i.e., physicalType XOR logicalType\n      +LogicalComplexType logicalType\n      +VarInt childCount %% Present only if CHILD_TYPES is set in columnOptions\n      +Field[] children\n    }\n\n    class ComplexType {\n      VEC_2 = 0\n      VEC_3 = 1\n      GEOMETRY = 2\n      GEOMETRY_Z = 3\n      LIST = 4\n      MAP = 5\n      STRUCT = 6\n    }\n\n    class LogicalComplexType {\n      BINARY = 0\n      RANGE_MAP = 1\n    }\n\n    class Column {\n    }\n\n    <<enumeration>> ComplexType\n    <<enumeration>> LogicalComplexType\n\n    note for ComplexColumn \"[EXPERIMENTAL]\"\n    note for ComplexField \"[EXPERIMENTAL]\"\n    note for ComplexType \"[EXPERIMENTAL]\"\n    note for LogicalComplexType \"[EXPERIMENTAL]\"\n    note for Field \"[EXPERIMENTAL]\"\n    note for ScalarField \"[EXPERIMENTAL]\"\n\n    Column --> ComplexColumn : complexType\n    ComplexColumn --> Field : children\n    ComplexField --> Field : children\n    Field --> ComplexField : complexField\n    Field --> ScalarField : scalarField\n```\n\n## Type System\n\nThe MLT type system distinguishes between physical and logical types.\nPhysical types define the data layout in storage, while logical types add semantic meaning.\nThis separation simplifies encoder and decoder implementation and allows encoding schemes to be reused.\n\n### Physical Types\n\nPhysical types define the data layout in storage.\nBoth scalar and complex types can be categorized as fixed-size or variable-size binaries.\nVariable-size binaries require an additional length stream to specify the size of each element.\nFixed-size binaries have a consistent bit (boolean) or byte width and thus require no length stream.\n\n**Scalar Types**\n\nEach scalar type uses a specific encoding scheme for its data stream.\n\n| Data Type                         | Logical Types               | Description                    | Layout      |\n|-------------------------------------------|---------------------------------|--------------------------------------|---------------|\n| Boolean                           |                         |                              | Fixed-Size    |\n| Int8, UInt8, Int32, UInt32, Int64, UInt64 | Date (int32), Timestamp (int64) |                              | Fixed-Size    |\n| Float, Double                       |                         |                              | Fixed-Size    |\n| String                            | JSON                      | UTF-8 encoded sequence of characters | Variable-Size |\n\n**Complex Types <span class=\"experimental\"></span>**\n\nComplex types are composed of scalar types.\n\n| Data Type      | Logical Types      | Description                              | Layout      |\n|------------------|----------------------|----------------------------------------------------|---------------|\n| List           | Binary (List<UInt8>) |                                        | Variable-Size |\n| Map            | Map<vec2d, T>      | Additional key stream -> length, key, data streams | Variable-Size |\n| Struct         |                  |                                        |             |\n| Vec2<T>, Vec3<T> | Geometry, GeometryZ  |                                        | Fixed-Size    |\n\n### Logical Types <span class=\"experimental\"></span>\n\n!!! CAUTION\n    Original text had `encodings can be reused` text which is unclear.\n    What is \"encodings\" in this context?\n\nLogical types add semantics on top of physical types, enabling code reuse and simplifying encoder/decoder implementation.\n\n| Logical Type | Physical Type      | Description                        |\n|--------------|----------------------|--------------------------------------------|\n| Date       | Int32            | Number of days since Unix epoch          |\n| Timestamp    | Int64            | Number of milliseconds since Unix epoch    |\n| RangeMap     | Map<vec2<Double>, T> | For storing linear referencing information |\n| Binary       | List<UInt8>        |                                  |\n| JSON       | String             |                                  |\n| Geometry     | vec2<Int32>        |                                  |\n| GeometryZ    | vec3<Int32>        |                                  |\n\n### Nested Fields Encoding\n\nFor nested properties (e.g., structs, lists), a [present/length](https://arxiv.org/pdf/2304.05028.pdf) pair encoding is chosen over the Dremel encoding for its simpler implementation and faster decoding into the in-memory format.\n\nEvery nullable field has an additional `present` stream.\nEvery collection type field (e.g., a list) has an additional `length` stream specifying its length.\nAs in ORC, nested fields are flattened based on a pre-order traversal.\n\nNested fields can also use shared dictionary encoding to share a common dictionary (e.g., for localized `name:*` columns in an OSM dataset).\nFields using a shared dictionary must be grouped sequentially in the file and prefixed by the dictionary.\n\n### RangeMap <span class=\"experimental\"></span>\n\n`RangeMaps` efficiently encode linear referencing information, as used in [Overture Maps](https://docs.overturemaps.org/overview/feature-model/scoping-rules#geometric-scoping-linear-referencing).\n`RangeSets` store range values and data values in two separate streams. The min and max values for the ranges are stored as interleaved double values in a separate range stream.\n\n## Encoding Schemes\n\nMLT uses various lightweight compression schemes for space-efficient storage and fast decoding.\nEncodings can be recursively cascaded (hybrid encodings) to a certain degree.\nFor example, integer columns resulting from dictionary encoding can be further compressed using integer encoding schemes.\n\nThe following encoding pool was selected based on analysis of compression ratio and decoding speed on test datasets like OpenMapTiles and Bing Maps tilesets.\n\n| Data Type | Logical Level Technique      | Physical Level Technique          | Description |\n| --------- | ---------------------------- | ----------------------------------- | ----------- |\n| Boolean   | [Boolean RLE](https://orc.apache.org/specification/ORCv1/#boolean-run-length-encoding) | | |\n| Integer   | Plain, RLE, Delta, Delta-RLE | [SIMD-FastPFOR](https://arxiv.org/pdf/1209.2137.pdf), [Varint](https://protobuf.dev/programming-guides/encoding/#varints) | |\n| Float     | Plain, RLE, Dictionary | | |\n| String    | Plain, Dictionary, [FSST](https://www.vldb.org/pvldb/vol13/p2649-boncz.pdf) Dictionary | | |\n| Geometry  | Plain, Dictionary, Morton-Dictionary | | |\n\n!!! NOTE\n    `FSST`, and `FastPFOR` encodings are <span class=\"experimental\"></span>.\n\nSIMD-FastPFOR is generally preferred over Varint encoding due to its smaller output and faster decoding speed.\nVarint encoding is included mainly for compatibility and simplicity, and it can be more efficient when combined with heavyweight compression like GZip.\n\nA brute-force search for the best encoding scheme is too costly.\nInstead, we recommend the selection strategy from the [BTRBlocks](https://www.cs.cit.tum.de/fileadmin/w00cfj/dis/papers/btrblocks.pdf) paper:\n\n- Calculate data metrics to exclude unsuitable encodings early (e.g., exclude RLE if the average run length is less than 2).\n- Use a sampling-based algorithm: randomly select parts of the data totaling ~1% of the full dataset and apply the candidate encodings from step 1.\n  Choose the scheme that produces the smallest output.\n\n## FeatureTable Layout\n\n### ID Column\n\nAn `id` column is not mandatory.\nIf included, it should be a u64 or narrower integer type (u32 if possible) for MVT compatibility.\nA narrower type enables the use of efficient encodings like FastPfor128.\n\n### Geometry Column\n\nThe geometry column uses a Structure of Arrays (SoA) layout (data-oriented design).\nThe `x`, `y`, and optional `z` coordinates are stored interleaved in a `VertexBuffer` for efficient CPU processing and direct copying to GPU buffers.\nIf the `z` coordinate is not needed for rendering, it can be stored separately as an `M-value` (see vertex-scoped properties).\n\nThe geometry information is separated into different streams, partly inspired by the [geoarrow](https://github.com/geoarrow/geoarrow) specification.\nThis separation enables better compression optimization and faster processing.\nPre-tessellated polygon meshes can also be stored directly to avoid runtime triangulation.\n\nA geometry column can consist of the following streams:\n\n| Stream Name    | Data Type | Encoding         | Mandatory |\n| -------------- | :-------: | ------------------ | :-------: |\n| GeometryType  |   Byte    | Integer |     ✓     |\n| NumGeometries |   UInt32  | Integer |         |\n| NumParts      |   UInt32  | Integer |         |\n| NumRings      |   UInt32  | Integer |         |\n| NumTriangles  |   UInt32  | Integer |         |\n| IndexBuffer   |   UInt32  | Integer |         |\n| VertexOffsets |   UInt32  | Integer |         |\n| VertexBuffer  | Int32 or Vertex[] | Plain, Dictionary, Morton |     ✓     |\n\nDepending on the geometry type, the following streams are used in addition to `GeometryType`:\n- **Point**: VertexBuffer\n- **LineString**: NumParts, VertexBuffer\n- **Polygon**: NumParts (Polygon), NumRings (LinearRing), VertexBuffer\n- **MultiPoint**: NumGeometries, VertexBuffer\n- **MultiLineString**: NumGeometries, NumParts (LineString), VertexBuffer\n- **MultiPolygon**: NumGeometries, NumParts (Polygon), NumRings (LinearRing), VertexBuffer\n\nAn additional `VertexOffsets` stream is present when using Dictionary or Morton-Dictionary encoding.\nIf geometries (mainly polygons) are pre-tessellated for direct GPU use, `NumTriangles` and `IndexBuffer` streams must be provided.\n\n### Property Columns\n\nFeature properties are divided into `feature-scoped` and `vertex-scoped` properties.\n- **Feature-scoped**: One value per feature.\n- **Vertex-scoped**: One value per vertex in the VertexBuffer per feature (modeling M-coordinates from GIS).\n\n!!! TODO\n    Would it make sense to place vertex-scoped properties AFTER feature scoped ones?\n    I suspect some implementations may act of feature-scoped properties first, and possibly even ignore vertex-scoped, at least for now (esp since vertex-scoped ones are still experimental)\n\nVertex-scoped properties must be grouped together and placed before feature-scoped properties in the FeatureTable.\nA property's scope is defined in the tileset metadata using the `ColumnScope` enum.\n\nA property column can use any data type from the [type system](#type-system).\n\n# Example Layouts\n\nThe following examples illustrate the layout of a `FeatureTable` in storage. The color scheme is:\n- **Blue boxes**:\n  Logical constructs, not persisted.\n  Fields are reconstructed from streams based on TileSet metadata.\n- **White boxes**:\n  Metadata describing data structure (FeatureTable, Stream (SM), Feature (FM) metadata).\n- **Yellow boxes**:\n  Streams containing the actual data.\n\n## Place Layer\n\nGiven a place [layer](assets/spec/place_feature.json) with the following JSON schema structure:\n![](assets/spec/place_feature.png)\n\nThe resulting MLT tile layout for this layer, using a dictionary for the `geometry` and `name` columns, might look like this:\n![img_2.png](assets/spec/img_2.png)\n\n## LineString Geometry with Flat Properties\n\nEncoding of a `FeatureTable` with an `id` field, a `LineString` geometry field, and the flat feature-scoped properties `class` and `subclass`:\n![img_4.png](assets/spec/img_4.png)\n\n## MultiPolygon with Flat Properties\n\nEncoding of a `FeatureTable` with an `id` field, a `MultiPolygon` geometry field, and flat feature-scoped property fields.\nA `VertexOffsets` stream is present due to vertex dictionary encoding:\n![img_5.png](assets/spec/img_5.png)\n\n## Vertex-Scoped and Feature-Scoped Properties\n\nExample layout encoding vertex-scoped and feature-scoped properties.\nAll vertex-scoped properties are grouped together and placed before feature-scoped properties.\nThe `id` column is not nullable, so its present stream is omitted.\n![img_7.png](assets/spec/img_7.png)\n\n# Sorting\n\nChoosing the right column to sort features by can significantly reduce the size of the `FeatureTable`.\nSorting is crucial for leveraging the columnar layout fully.\nExhaustively testing every possible sorting order for every column in every layer is computationally expensive.\nSee recommended heuristic in the [encoding schemes](#encoding-schemes).\n\n# Encodings\n\n!!! TODO\n    inline encodings here\n\nEncoding details are specified in [a separate document](encodings.md).\n\n# In-Memory Format\n\n!!! NOTE\n    The following is a high-level overview; the in-memory format will be explained in more detail later.\n\nThe record-oriented, array-of-structures in-memory model used by libraries processing Mapbox Vector Tiles incurs considerable overhead.\nThis includes creating many small objects (increasing memory allocation load) and placing additional strain on garbage collectors in browsers.\n\nMLT uses a columnar memory layout (data-oriented design) for its in-memory format to overcome these issues.\nThis approach improves cache utilization for subsequent data access and enables the use of fast SIMD instructions.\nThe MLT in-memory format incorporates ideas from analytical in-memory formats like Apache Arrow, Velox, and the DuckDB execution format, tailored for visualization use cases.\nIt is also designed for future parallel processing on the GPU within compute shaders.\n\nThe main design goals for the MLT in-memory format are:\n- Define a platform-agnostic representation to avoid expensive materialization costs, especially for strings.\n- Maximize CPU throughput by optimizing memory layout for cache locality and SIMD instructions.\n- Allow random (preferably constant-time) access to all data for parallel processing on GPUs (compute shaders).\n- Provide compressed data structures that can be processed directly without full decoding.\n- Provide tile geometries in a representation that can be loaded into GPU buffers with minimal additional processing.\n\nData is stored in contiguous memory buffers called **vectors**, accompanied by metadata and an optional null bitmap.\nThe storage format includes a `VectorType` field in the metadata to instruct the decoder which vector type to use for a specific field.\nAn auxiliary offset buffer enables random access to variable-sized data types like strings or lists.\n\nThe MLT in-memory format supports the following vector types:\n\n- [Flat Vectors](https://duckdb.org/internals/vector.html#flat-vectors)\n- [Constant Vectors](https://duckdb.org/internals/vector.html#constant-vectors)\n- [Sequence Vectors](https://duckdb.org/internals/vector.html#sequence-vectors)\n- [Dictionary Vectors](https://duckdb.org/internals/vector.html#dictionary-vectors)\n- FSST Dictionary Vectors <span class=\"experimental\"></span>\n- Shared Dictionary Vectors <span class=\"experimental\"></span>\n- [Run-End Encoded (REE) Vectors](https://arrow.apache.org/docs/format/Columnar.html#run-end-encoded-layout)\n\n!!! NOTE\n    Further evaluation is needed to determine if [recent research](https://arxiv.org/pdf/2306.15374.pdf) can enable random access on delta-encoded values.\n\nUsing a compressed vector where possible makes the conversion from storage to in-memory format essentially a zero-copy operation.\n\nFollowing Apache Arrow's approach and the [Intel performance guide](https://www.intel.com/content/www/us/en/developer/topic-technology/data-center/overview.html), decoders should allocate memory on addresses aligned to a 64-byte multiple (where possible).\n"
  },
  {
    "path": "java/.gitignore",
    "content": ".gradle/\nbuild/\noutput/\n*.mlt.mbtiles\n*.mlt.pmtiles\n"
  },
  {
    "path": "java/CONTRIBUTING.md",
    "content": "To build the project run the following command:\n````bash\n./gradlew build\n````\n\nTo format the code run the following command:\n````bash\n./gradlew spotlessApply\n````\n\nTo execute the benchmarks run the following command:\n````bash\n./gradlew jmh\n````\n\nTo build just the CLI tools:\n````bash\n./gradlew cli\n````\n\nTo run the tests:\n````bash\n./gradlew test\n````\n\nTo run specific tests like the `MltDecoderTest` test:\n````bash\n./gradlew test --tests com.mlt.decoder.MltDecoderTest\n````\n\nView test reports by opening `build/reports/tests/test/index.html`\n\nView test coverage reports by opening `build/reports/jacoco/test/html/index.html`\n"
  },
  {
    "path": "java/README-Decode.md",
    "content": "## MLT Decoder Java CLI Application\n\nThis is a very simple application which serves as an example of how to use the MLT library to decode MLT files.\n\nIt can also be used to benchmark the performance of the MLT decoder by timing the repeated decoding of a single tile.\n"
  },
  {
    "path": "java/README-Encode.md",
    "content": "## MLT Encoder Java CLI Application\n\nThis application currently only supports converting MVT tiles to MLT format.\n\nIt can convert standalone tile files, MBTiles files, MLN Offline database files, and PMTiles files.\n\n### Usage\n\nSee the `--help` output for the full list of options.\n\nAt a minimum, the input file(s) must be specified with `--mvt`, `--mbtiles`, `--offlinedb`, or `--pmtiles`.\n\nIn the case of `--pmtiles`, the input may be a URL, in which case the data will be selectively downloaded, allowing rapid conversion of zoom-level subsets.\n\nBy default, the output file will be the input basename with the `.mlt` extension added, placed in the working directory.  This can be overridden with the `--mlt` option.  The `--dir` option changes the output directory.\n\nFor efficient conversion the `--parallel` option should generally be used to take advantage of all available CPU cores.\n\n### Environment Variables\n\n#### Common\n\n- `MLT_TILE_LOG_INTERVAL`: Controls the number of tiles between progress reports with `--verbose`\n\n#### MBTiles\n\nTile compression within an MBTiles file is optional.  The encoder will only store compressed MLT tiles if they are slightly smaller than the uncompressed MLT data.  To be stored compressed, a tile must meet both criteria, as controlled by the following environment variables:\n\n- `MLT_COMPRESSION_RATIO_THRESHOLD`: Minimum compression ratio to apply compression (default: 0.98)\n  - If compressed tile is larger than this fraction of the uncompressed tile, it will be discarded and the uncompressed tile will be stored instead.\n  - This value may be greater than 1.0 to indicate that tiles should be stored in compressed form even if it increases their size.\n- `MLT_COMPRESSION_FIXED_THRESHOLD`: Minimum savings in bytes for a tile to be compressed (default:  20)\n  - If the compressed tile size plus this value is greater than the uncompressed tile size, the compressed tile will be discarded and the uncompressed tile will be stored instead.\n  - This value can be negative to indicate that tiles should be stored in compressed form even if they are larger than the uncompressed tile.\n\n#### PMTiles\n\nPMTiles files can be extremely large, and may benefit from careful selection of extended configuration parameters.\n\nPMTiles conversion can use substantial memory, especially for large archives, high cache sizes, and many parallel worker threads.\n\n- `MLT_CACHE_MAX`: Maximum cache size in bytes.\n- `MLT_CACHE_MAX_HEAP_PERCENT`: Maximum cache size as a percentage of maximum heap size.\n  - If a percentage is specified, it will take precedence over `MLT_CACHE_MAX`.\n- `MLT_CACHE_EXPIRE`: Cache expiration duration after access, in ISO-8601 (e.g. P1.2S) or plain (e.g., 1.2s).\n  - This is generally not important, but may decrease memory use with a very large cache in long-running conversions.\n- `MLT_CACHE_BLOCK_SIZE`: Aligned block size of cached data, in bytes\n  - Cache misses will trigger a read of the entire block(s) containing the tile, often allowing reads of nearby tiles without an additional request.\n  - When zero, only the requested tile will be read on a cache miss.  This is not recommended.\n  - Larger block sizes mean fewer entries in the cache, but tend to be more efficient as the main benefit is from other tiles in the same block.\n- `MLT_CACHE_AVERAGE_SIZE`: Average size of tiles in bytes\n  - This is used to estimate the number of tiles that can be stored in the cache.\n  - A value of zero disables cache pre-allocation.\n- `MLT_MAX_TILE_TRACK_SIZE`: Maximum size of a tile to be tracked in memory, in bytes.\n  - PMTiles files combine ranges of identical tiles (in Hilbert index order) into a single directory entry. If the same tile contents appears elsewhere, however, it will be stored as a separate entry. The encoder keeps track of each input tile in order to avoid re-converting it if it's encountered later, reducing the computation needed at the cost of some memory.\n  - Only small tiles, e.g., representing empty ocean, are commonly duplicated and so worth tracking.  So to save memory, only tiles below this threshold are tracked.\n  - Set to zero to disable tracking altogether.\n- `MLT_THREAD_QUEUE_SIZE`: The maximum number of items (per worker thread) to queue when parallel processing is enabled.\n  - Larger values cause the directory traversal to finish earlier allowing for a progress percentage to be displayed, at the cost of more memory use.\n  - Very small values may lead to queue starvation and reduced performance.\n\nIn addition, it may be beneficial to adjust the JVM heap parameters to allow for a heap size larger than the default, e.g.:\n  - `-Xmx32G` to allow up to 32 GB of heap memory\n  - `-Xms16G` to pre-allocate 16 GB of heap memory at startup\n\nCache hits are more important when using a remote source, but a very large cache (1GB+) seems to be counterproductive.\n\nUse `--cache-stats` to print cache statistics to help tune the cache configuration parameters.\n"
  },
  {
    "path": "java/README.MD",
    "content": "# MapLibre Tile Java\n\n[![Maven Central Version](https://img.shields.io/maven-central/v/org.maplibre/mlt)](https://central.sonatype.com/artifact/org.maplibre/mlt) ![GitHub License](https://img.shields.io/github/license/maplibre/maplibre-tile-spec) [![test](https://github.com/maplibre/maplibre-tile-spec/actions/workflows/test.yml/badge.svg)](https://github.com/maplibre/maplibre-tile-spec/actions/workflows/test.yml)\n\nA Java library for encoding and decoding MapLibre Tile (MLT) format, providing efficient compression and fast access to vector tile data.\n\n## Project Structure\n\nThis is a multi-project Gradle build with two main components:\n\n- **`mlt-core`** - The core library containing the MLT encoding/decoding functionality\n- **`mlt-cli`** - Command-line tools for converting between MVT and MLT formats\n- **`mlt-tools`** - Utilities for working with MLT, currently only used for testing\n\nThe core library is published to Maven Central, while the CLI tools are built locally for development use.\n\n## Installation\n\n### Gradle (`build.gradle`)\n\n```gradle\nrepositories {\n  mavenCentral()\n  maven {\n    url 'https://maven.ecc.no/releases'\n  }\n}\n\ndependencies {\n  implementation 'org.maplibre:mlt:0.0.10'\n}\n```\n\n### Gradle (`build.gradle.kts`)\n\n```kotlin\nrepositories {\n  mavenCentral()\n  maven {\n    url = uri(\"https://maven.ecc.no/releases\")\n  }\n}\n\ndependencies {\n  implementation(\"org.maplibre:mlt:0.0.10\")\n}\n```\n\n### Maven\n\n```xml\n<repositories>\n  <repository>\n    <id>maven-central</id>\n    <url>https://repo1.maven.org/maven2</url>\n  </repository>\n  <repository>\n    <id>ecc-releases</id>\n    <url>https://maven.ecc.no/releases</url>\n  </repository>\n</repositories>\n\n<dependencies>\n  <dependency>\n    <groupId>org.maplibre</groupId>\n    <artifactId>mlt</artifactId>\n    <version>0.0.10</version>\n  </dependency>\n</dependencies>\n```\n\nSee the [Maven Central Repository](https://mvnrepository.com/artifact/org.maplibre/mlt) for the latest published versions and syntax for other build systems.\n\n## Usage Examples\n\n### Basic MLT Encoding\n\nConvert a Mapbox Vector Tile (MVT) to MLT format:\n\n```java\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\n\nbyte[] mvtData = Files.readAllBytes(Path.of(\"tile.mvt\"));\nvar mvtTile = MvtUtils.decodeMvt(mvtData);\n\n// Configure column mappings for nested properties (e.g., name:en, name:de)\nvar columnMappingConfig = List.of(new ColumnMapping(\"name\", \":\", true));\n\nvar tilesetMetadata = MltConverter.createTilesetMetadata(mvtTile, columnMappingConfig, true);\n\nvar optimization = new FeatureTableOptimizations(false, false, columnMappingConfig);\nvar optimizations = Map.of(\"layer_name\", optimization);\nvar config = new ConversionConfig(true, true, optimizations);\n\nbyte[] mltData = MltConverter.convertMvt(mvtTile, tilesetMetadata, config, null);\n\nFiles.write(Path.of(\"tile.mlt\"), mltData);\n```\n\n### Basic MLT Decoding\n\nThe simplest way to decode an MLT tile:\n\n```java\nimport org.maplibre.mlt.decoder.MltDecoder;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nbyte[] mltData = Files.readAllBytes(Path.of(\"tile.mlt\"));\n\nMapLibreTile tile = MltDecoder.decodeMlTile(mltData);\n\nfor (var layer : tile.layers()) {\n  System.out.println(\"Layer: \" + layer.name());\n  System.out.println(\"Features: \" + layer.features().size());\n\n  for (var feature : layer.features()) {\n    System.out.println(\"Feature ID: \" + feature.id());\n    System.out.println(\"Geometry: \" + feature.geometry().getGeometryType());\n    System.out.println(\"Properties: \" + feature.properties());\n  }\n}\n```\n\n### Advanced Encoding with Optimizations\n\nFor better compression, you can enable advanced encoding schemes and sorting:\n\n```java\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport java.util.Map;\n\nvar optimization = new FeatureTableOptimizations(true, true, columnMappingConfig);\nvar optimizations = Map.of(\n  \"water\", optimization,\n  \"roads\", optimization,\n  \"buildings\", optimization\n);\n\nvar config = new ConversionConfig(true, true, optimizations);\n\nbyte[] mltData = MltConverter.convertMvt(mvtTile, tilesetMetadata, config, null);\n```\n\n### Working with Feature Properties\n\nAccess and manipulate feature properties:\n\n```java\nimport org.maplibre.mlt.decoder.MltDecoder;\nimport org.maplibre.mlt.data.MapLibreTile;\n\nMapLibreTile tile = MltDecoder.decodeMlTile(mltData);\n\nfor (var layer : tile.layers()) {\n  for (var feature : layer.features()) {\n    var properties = feature.properties();\n\n    String name = (String) properties.get(\"name\");\n    Integer population = (Integer) properties.get(\"population\");\n    Boolean isCapital = (Boolean) properties.get(\"is_capital\");\n\n    // Work with nested properties (if using column mappings)\n    String nameEn = (String) properties.get(\"name:en\");\n    String nameDe = (String) properties.get(\"name:de\");\n\n    properties.put(\"processed\", true);\n  }\n}\n```\n\n### Batch Processing\n\nProcess multiple tiles efficiently:\n\n```java\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.stream.Stream;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\ntry (Stream<Path> paths = Files.list(Paths.get(\"input_tiles\"))) {\n  paths.filter(path -> path.toString().endsWith(\".mvt\"))\n       .forEach(mvtPath -> {\n         try {\n           byte[] mvtData = Files.readAllBytes(mvtPath);\n           var mvtTile = MvtUtils.decodeMvt(mvtData);\n           var tilesetMetadata = MltConverter.createTilesetMetadata(mvtTile, List.of(), true);\n           var config = new ConversionConfig(true, true, Map.of());\n           byte[] mltData = MltConverter.convertMvt(mvtTile, tilesetMetadata, config, null);\n\n           String mltPath = mvtPath.toString().replace(\".mvt\", \".mlt\");\n           Files.write(Paths.get(mltPath), mltData);\n\n           System.out.println(\"Converted: \" + mvtPath + \" -> \" + mltPath);\n         } catch (IOException e) {\n           System.err.println(\"Error processing \" + mvtPath + \": \" + e.getMessage());\n         }\n       });\n}\n```\n\n## CLI Usage\n\nTo build command line tools:\n\n```console\n./gradlew cli\n```\n\nThis will create two executable JAR files in `mlt-cli/build/libs/`:\n- `encode.jar` - Convert MVT files to MLT format\n- `decode.jar` - Decode MLT files\n\nExample of converting MVT to MLT:\n\n```console\njava -jar mlt-cli/build/libs/encode.jar --mvt ../test/fixtures/simple/point-boolean.pbf --mlt /tmp/point-boolean.mlt --tessellate --outlines ALL --verbose\n\njava -jar mlt-cli/build/libs/encode.jar --mbtiles /path/to/input.mbtiles --mlt /tmp/output.mbtiles --tessellate --outlines ALL --compress=deflate --verbose\n\njava -jar mlt-cli/build/libs/encode.jar --pmtiles /path/to/input.pmtiles --mlt /tmp/output.pmtiles --tessellate --outlines ALL --parallel\n```\n\nExample of decoding MLT files:\n\n```console\njava -jar mlt-cli/build/libs/decode.jar --mlt /tmp/point-boolean.mlt\n```\n\nFor more details, see [README-Encode.md](README-Encode.md) and [README-Decode.md](README-Decode.md).\n\n## Contributing\n\nRun the tests:\n\n```\njust java::test\njust java::test-cli\n```\n"
  },
  {
    "path": "java/encoding-server/.gitignore",
    "content": "cache/\nnode_modules/\n"
  },
  {
    "path": "java/encoding-server/README.md",
    "content": "# MVT to MLT Development Server\n\nThis Node.js-based application serves as a **development and testing server** for converting **MVT** to **MLT** in realtime.\nThe server acts as a proxy for style, source, and tile endpoints, enabling seamless on-demand transformation of vector tile data.\n\n> Most of the arguments supported by the java encoder are also available as Node.js arguments or through `config.json` file.\n\n---\n\n## Prerequisites\n\n- Node.js\n- npm (Node package manager)\n- Java MLT encoder\n\n## Running the Server\n\n```bash\ncd encoding-server\nnpm install\nnpm start\n```\n\n## Requests\n\nIn most cases, the initial style request is sufficient to initiate the process, with all subsequent source and tile requests automatically redirected back to the server.\n\n```bash\n## style - http://<server_ip>/style?url=<style_url>\ncurl http://0.0.0.0/style?url=https://demotiles.maplibre.org/style.json ## default\ncurl http://localhost/style?url=https://demotiles.maplibre.org/style.json\ncurl http://10.0.2.2/style?url=https://demotiles.maplibre.org/style.json ## Android emulator bridge to 0.0.0.0\n\n## source - http://<server_ip>/style?url=<source_url>\ncurl http://0.0.0.0/source?url=https://demotiles.maplibre.org/tiles/tiles.json\n\n## tile - http://<server_ip>/tile?url=<tile_url>\ncurl http://0.0.0.0/tile?url=https://demotiles.maplibre.org/tiles/{z}/{x}/{y}.pbf\n\n```\n"
  },
  {
    "path": "java/encoding-server/config.json",
    "content": "{\n  \"host\": \"0.0.0.0\",\n  \"port\": 80,\n  \"verbose\": true,\n\n  \"input\": \"mvt\",\n  \"noids\": false,\n  \"fsst\": false,\n  \"fastpfor\": false,\n  \"nomorton\": false,\n  \"outlines\": \"ALL\",\n  \"tessellate\": false,\n  \"coercemismatch\": true,\n  \"timer\": false,\n  \"compare\": false\n}\n"
  },
  {
    "path": "java/encoding-server/config.mjs",
    "content": "import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst configFile = JSON.parse(\n  fs.readFileSync(path.join(__dirname, \"config.json\")),\n);\n\nconst { values: args } = parseArgs({\n  options: {\n    host: { type: \"string\", default: \"0.0.0.0\" },\n    port: { type: \"string\", default: \"80\" },\n    verbose: { type: \"boolean\", default: true },\n    keep_files: { type: \"boolean\", default: false },\n    noencodingserver: { type: \"boolean\", default: false },\n    input: { type: \"string\", default: \"mvt\" },\n    noids: { type: \"boolean\", default: false },\n    fsst: { type: \"boolean\", default: false },\n    fastpfor: { type: \"boolean\", default: false },\n    nomorton: { type: \"boolean\", default: false },\n    tessellate: { type: \"boolean\", default: false },\n    outlines: { type: \"string\", default: \"ALL\" },\n    coercemismatch: { type: \"boolean\", default: true },\n    timer: { type: \"boolean\", default: false },\n    compare: { type: \"boolean\", default: false },\n  },\n  args: process.argv.slice(2),\n});\n\nconst possibleInputs = [\"mvt\", \"pmtiles\"];\nif (!possibleInputs.includes(args.input)) {\n  console.error(`--input must be one of ${possibleInputs.join(\", \")}`);\n  process.exit(1);\n}\n\nconst config = {\n  ...args,\n  ...configFile,\n  port: Number(configFile.port ?? args.port ?? 80),\n};\n\nconfig.cachePath = path.join(__dirname, \"cache\");\nconfig.cliToolsPath = path.join(__dirname, \"../\");\nconfig.encoderPath = path.join(\n  config.cliToolsPath,\n  \"mlt-cli/build/libs/encode.jar\",\n);\nconfig.encoderPort = 3001;\n\nexport default config;\n"
  },
  {
    "path": "java/encoding-server/convert.mjs",
    "content": "import { exec, execSync, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { createWriteStream, existsSync, mkdirSync, unlink } from \"node:fs\";\nimport net from \"node:net\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\n\nimport config from \"./config.mjs\";\n\nfunction convertRequest(convertResponse) {\n  return (req, res) => {\n    if (config.verbose) {\n      console.log(req.originalUrl);\n    }\n\n    if (!req.query.url) {\n      if (config.verbose) {\n        console.error(\"Missing `url` parameter\");\n      }\n\n      res.status(400).send(\"Missing `url` parameter\");\n      return;\n    }\n\n    let url;\n    try {\n      url = new URL(req.query.url);\n    } catch {\n      if (config.verbose) {\n        console.error(`Invalid url ${req.query.url}`);\n      }\n\n      res.status(400).send(`Invalid url: ${req.query.url}`);\n      return;\n    }\n\n    fetch(url)\n      .then((styleResponse) => styleResponse.text())\n      .then((data) => convertResponse(req, data, res))\n      .catch((error) => {\n        if (config.verbose) {\n          console.error(`Request failed: ${req.query.url} - ${error}`);\n        }\n        res.status(500).send(`Request failed: ${req.query.url} - ${error}`);\n      });\n  };\n}\n\nfunction convertURL(urlString, type, req) {\n  try {\n    const url = new URL(urlString);\n\n    if (url.protocol !== \"https:\" && url.protocol !== \"http:\") {\n      return urlString;\n    }\n  } catch (error) {\n    if (config.verbose) {\n      console.error(`URL (${urlString}) parse error: ${error}`);\n    }\n    return urlString;\n  }\n\n  return `${req.protocol}://${req.get(\"host\")}/${type}?url=${urlString}`;\n}\n\nfunction convertStyleResponse(req, data, res) {\n  try {\n    const json = JSON.parse(data);\n\n    if (json.sources) {\n      for (const key in json.sources) {\n        if (!Object.hasOwn(json.sources, key)) {\n          continue;\n        }\n\n        const source = json.sources[key];\n\n        if (!source || source.type !== \"vector\") {\n          continue;\n        }\n\n        source.encoding = \"mlt\";\n\n        if (source.url) {\n          source.url = convertURL(source.url, \"source\", req);\n        }\n\n        if (source.tiles) {\n          source.tiles = source.tiles.map((tile) => {\n            return convertURL(tile, \"tile\", req);\n          });\n        }\n      }\n    }\n\n    res.status(200).json(json);\n  } catch (error) {\n    if (config.verbose) {\n      console.error(`Failed to parse style response: ${error}`);\n    }\n    res.status(400).send(`Failed to parse style response: ${error}`);\n  }\n}\n\nfunction convertSourceResponse(req, data, res) {\n  try {\n    const json = JSON.parse(data);\n\n    json.encoding = \"mlt\";\n\n    if (json.tiles) {\n      for (const key in json.tiles) {\n        if (!Object.hasOwn(json.tiles, key)) {\n          continue;\n        }\n\n        json.tiles[key] = convertURL(json.tiles[key], \"tile\", req);\n      }\n    }\n\n    res.status(200).json(json);\n  } catch (error) {\n    if (config.verbose) {\n      console.error(`Failed to parse style response: ${error}`);\n    }\n    res.status(400).send(`Failed to parse style response: ${error}`);\n  }\n}\n\nconst convertStyleRequest = convertRequest(convertStyleResponse);\nconst convertSourceRequest = convertRequest(convertSourceResponse);\n\nfunction convertTileResponse(filePath, res) {\n  const mltPath = `${filePath}.mlt`;\n  const args =\n    \" --\" +\n    config.input +\n    \" \" +\n    filePath +\n    \" --mlt \" +\n    mltPath +\n    (config.noids ? \" --noids\" : \"\") +\n    (config.fsst ? \" --fsst\" : \"\") +\n    (config.fastpfor ? \" --fastpfor\" : \"\") +\n    (config.nomorton ? \" --nomorton\" : \"\") +\n    (config.outlines ? ` --outlines ${config.outlines}` : \"\") +\n    (config.tessellate ? \" --tessellate\" : \"\") +\n    (config.coercemismatch ? \" --coerce-mismatch\" : \"\") +\n    (config.timer ? \" --timer\" : \"\") +\n    (config.compare ? \" --compare-all\" : \"\");\n\n  const callback = (error, stdout, stderr) => {\n    if (config.verbose) {\n      if (stdout) {\n        console.log(`Encoder output: ${stderr}`);\n      }\n\n      if (stderr) {\n        console.error(`Encoder error: ${stderr}`);\n      }\n    }\n\n    if (!config.keep_files) {\n      unlink(filePath, (fileErr) => {\n        if (fileErr && config.verbose) {\n          console.error(\n            `Failed to delete input file: ${filePath} - ${fileErr}`,\n          );\n        }\n      });\n    }\n\n    if (error) {\n      if (config.verbose) {\n        console.error(`Tile encoding failed: ${error}`);\n      }\n\n      res.status(500).send(`Tile encoding failed: ${error}`);\n      return;\n    }\n\n    res.on(\"finish\", () => {\n      if (!config.keep_files) {\n        unlink(mltPath, (fileErr) => {\n          if (fileErr && config.verbose) {\n            console.error(\n              `Failed to delete output file: ${mltPath} - ${fileErr}`,\n            );\n          }\n        });\n      }\n    });\n\n    res.status(200).sendFile(mltPath);\n  };\n\n  if (config.noencodingserver) {\n    convertTileCLI(args, callback);\n  } else {\n    convertTileCLIServer(args, callback);\n  }\n}\n\nfunction convertTileCLI(args, callback) {\n  const command = `java -jar ${config.encoderPath} ${args}`;\n  exec(command, callback);\n}\n\nfunction convertTileCLIServer(args, callback) {\n  const command = `${args}\\n`;\n  let response = \"\";\n  const socket = new net.Socket();\n\n  socket.connect(config.encoderPort, \"localhost\", () => {\n    socket.write(command);\n  });\n\n  socket.on(\"data\", (data) => {\n    response += data;\n  });\n\n  socket.on(\"close\", () => {\n    callback(null, response.length > 0 ? response : null, null);\n  });\n\n  socket.on(\"error\", (error) => {\n    console.error(`Encoder error: ${error}`);\n  });\n}\n\nfunction convertTileRequest(req, res) {\n  if (config.verbose) {\n    console.log(req.originalUrl);\n  }\n\n  if (!req.query.url) {\n    if (config.verbose) {\n      console.error(\"Missing `url` parameter\");\n    }\n\n    res.status(400).send(\"Missing `url` parameter\");\n    return;\n  }\n\n  let url;\n  try {\n    url = new URL(req.query.url);\n  } catch {\n    if (config.verbose) {\n      console.error(`Invalid url: ${req.query.url}`);\n    }\n\n    res.status(400).send(`Invalid url: ${req.query.url}`);\n    return;\n  }\n\n  fetch(url)\n    .then(async (tileResponse) => {\n      if (!tileResponse.ok) {\n        res\n          .status(tileResponse.status)\n          .send(\n            `Tile request error: ${req.query.url} - ${tileResponse.status} ${tileResponse.statusText}`,\n          );\n        return;\n      }\n\n      if (!existsSync(config.cachePath)) {\n        mkdirSync(config.cachePath, { recursive: true });\n      }\n\n      const file = createWriteStream(join(config.cachePath, randomUUID()));\n\n      try {\n        await pipeline(Readable.fromWeb(tileResponse.body), file);\n      } catch (error) {\n        if (config.verbose) {\n          console.error(`Tile download failed: ${req.query.url} - ${error}`);\n        }\n        res\n          .status(500)\n          .send(`Tile download failed: ${req.query.url} - ${error}`);\n        return;\n      }\n\n      convertTileResponse(file.path, res);\n    })\n    .catch((error) => {\n      if (config.verbose) {\n        console.error(`Request failed: ${req.query.url} - ${error}`);\n      }\n      res.status(500).send(`Request failed: ${req.query.url} - ${error}`);\n    });\n}\n\nfunction runCLISetup() {\n  console.log(`Building CLI tools at ${config.cliToolsPath}`);\n  execSync(\"./gradlew cli\", { cwd: config.cliToolsPath });\n\n  if (config.noencodingserver) {\n    return;\n  }\n\n  const server = spawn(\"java\", [\n    \"-jar\",\n    `${config.encoderPath}`,\n    \"--server\",\n    `${config.encoderPort}`,\n  ]);\n\n  if (config.verbose) {\n    server.stdout.on(\"data\", (data) => {\n      console.log(`Encoder: ${data}`);\n    });\n\n    server.stderr.on(\"data\", (data) => {\n      console.error(`Encoder: ${data}`);\n    });\n  }\n\n  server.on(\"close\", (code) => {\n    console.log(`Encoder closed: ${code}`);\n  });\n}\n\nexport {\n  convertSourceRequest,\n  convertStyleRequest,\n  convertTileRequest,\n  runCLISetup,\n};\n"
  },
  {
    "path": "java/encoding-server/eslint.config.mjs",
    "content": "import eslint from \"@eslint/js\";\nimport nPlugin from \"eslint-plugin-n\";\nimport globals from \"globals\";\n\nexport default [\n  eslint.configs.recommended,\n  {\n    plugins: {\n      n: nPlugin,\n    },\n    languageOptions: {\n      globals: {\n        ...globals.node,\n      },\n    },\n    rules: {\n      \"n/prefer-node-protocol\": \"error\",\n    },\n  },\n  {\n    ignores: [\"node_modules/**\", \"cache/**\"],\n  },\n];\n"
  },
  {
    "path": "java/encoding-server/package.json",
    "content": "{\n  \"name\": \"encoding-server\",\n  \"version\": \"1.0.0\",\n  \"main\": \"server.mjs\",\n  \"scripts\": {\n    \"start\": \"node server.mjs\",\n    \"lint\": \"eslint .\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"description\": \"\",\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^5.2.0\",\n    \"https\": \"^1.0.0\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.38.0\",\n    \"eslint\": \"^9.38.0\",\n    \"eslint-plugin-n\": \"^17.23.1\",\n    \"globals\": \"^16.4.0\"\n  }\n}\n"
  },
  {
    "path": "java/encoding-server/server.mjs",
    "content": "import cors from \"cors\";\nimport express from \"express\";\n\nimport config from \"./config.mjs\";\nimport {\n  convertSourceRequest,\n  convertStyleRequest,\n  convertTileRequest,\n  runCLISetup,\n} from \"./convert.mjs\";\n\nconst app = express();\n\napp.use(cors());\n\napp.use(\"/style\", convertStyleRequest);\napp.use(\"/source\", convertSourceRequest);\napp.use(\"/tile\", convertTileRequest);\n\napp.listen(config.port, config.host, () => {\n  runCLISetup();\n\n  console.log(`Server started on port ${config.port}`);\n});\n"
  },
  {
    "path": "java/gradle/libs.versions.toml",
    "content": "[versions]\ntileverse = \"1.3.3\"\njmh = \"1.37\"\njunit-jupiter = \"6.0.3\"\nkotlin-jvm = \"2.3.10\"\nlombok = \"1.18.38\"\n\n[libraries]\ncommons-cli = { group = \"commons-cli\", name = \"commons-cli\", version = \"1.11.0\"}\ncommons-compress = { group = \"org.apache.commons\", name = \"commons-compress\", version = \"1.28.0\" }\ncommons-lang3 = { group = \"org.apache.commons\", name = \"commons-lang3\", version = \"3.20.0\" }\nearcut4j = { group = \"org.maplibre\", name = \"earcut4j\", version = \"3.0.0\" }\ngson = { group = \"com.google.code.gson\", name = \"gson\", version = \"2.13.2\" }\nguava = { group = \"com.google.guava\", name = \"guava\", version = \"33.5.0-jre\" }\nhppc = { group = \"com.carrotsearch\", name = \"hppc\", version = \"0.10.0\" }\njackson-databind = { group = \"com.fasterxml.jackson.core\", name = \"jackson-databind\", version = \"2.21.1\" }\njavaFastPFOR = { group = \"me.lemire.integercompression\", name = \"JavaFastPFOR\", version = \"0.3.10\" }\njakarta-annotation-api = { group = \"jakarta.annotation\", name = \"jakarta.annotation-api\", version = \"3.0.0\" }\njava-vector-tile = { group = \"no.ecc.vectortile\", name = \"java-vector-tile\", version = \"1.3.23\" }\njetbrains-annotations = { group = \"org.jetbrains\", name = \"annotations\", version = \"26.1.0\" }\njmh-core = { group = \"org.openjdk.jmh\", name = \"jmh-core\", version.ref = \"jmh\" }\njmh-generator-annprocess = { group = \"org.openjdk.jmh\", name = \"jmh-generator-annprocess\", version.ref = \"jmh\"}\njts-core = { group = \"org.locationtech.jts\", name = \"jts-core\", version = \"1.20.0\" }\njts-io-common = { group = \"org.locationtech.jts.io\", name = \"jts-io-common\", version = \"1.20.0\" }\njunit-jupiter-api = { group = \"org.junit.jupiter\", name = \"junit-jupiter-api\", version.ref = \"junit-jupiter\" }\njunit-jupiter-engine = { group = \"org.junit.jupiter\", name = \"junit-jupiter-engine\", version.ref = \"junit-jupiter\" }\njunit-jupiter-params = { group = \"org.junit.jupiter\", name = \"junit-jupiter-params\", version.ref = \"junit-jupiter\" }\njunit-jupiter-platform-launcher = { group = \"org.junit.platform\", name = \"junit-platform-launcher\", version.ref = \"junit-jupiter\"}\nkotlin-stdlib = { group = \"org.jetbrains.kotlin\", name = \"kotlin-stdlib\", version.ref = \"kotlin-jvm\" }\nmapbox-vector-tile-java = { group = \"io.github.sebasbaumh\", name = \"mapbox-vector-tile-java\", version = \"25.1.0\" }\nmbtiles4j = { group = \"org.imintel\", name = \"mbtiles4j\", version = \"1.0.6\" }\nplanetiler-core = { module= \"com.onthegomap.planetiler:planetiler-core\", version=\"0.10.0\" }\nprotobuf-java = { group = \"com.google.protobuf\", name = \"protobuf-java\", version = \"4.34.0\" }\nlog4j-api = { group = \"org.apache.logging.log4j\", name = \"log4j-api\", version = \"2.25.3\" }\nlog4j-slf4j-impl = { group = \"org.apache.logging.log4j\", name = \"log4j-slf4j-impl\", version = \"2.25.3\" }\nsqlite-jdbc = { group = \"org.xerial\", name = \"sqlite-jdbc\", version = \"3.51.2.0\" }\ntileverse-io = { group = \"io.tileverse\", name = \"tileverse-bom\", version.ref = \"tileverse\" }\ntileverse-rangereader-all = { module= \"io.tileverse.rangereader:tileverse-rangereader-all\" }\nlombok = { group = \"org.projectlombok\", name = \"lombok\", version.ref = \"lombok\" }\n\n[plugins]\ndiffplug-spotless = { id = \"com.diffplug.spotless\", version = \"8.2.1\"}\ngradleup-shadow = { id = \"com.gradleup.shadow\", version = \"9.3.2\"}\nme-champeau-jmh = { id = \"me.champeau.jmh\", version = \"0.7.3\" }\nmaven-publish = { id = \"com.vanniktech.maven.publish\", version = \"0.36.0\" }\nkotlin-jvm = { id = \"org.jetbrains.kotlin.jvm\", version.ref = \"kotlin-jvm\" }\n"
  },
  {
    "path": "java/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.3.1-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "java/gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "java/gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "java/lombok.config",
    "content": "# Add generated annotations to generated code\nlombok.addLombokGeneratedAnnotation = true\n# Add nullability annotations to generated code\nlombok.addNullAnnotations = jspecify\n# Suppress warnings on generated code\nlombok.addSuppressWarnings = true\n"
  },
  {
    "path": "java/mlt-cli/build.gradle",
    "content": "plugins {\n    id 'java-library'\n    id 'jacoco'\n    alias(libs.plugins.kotlin.jvm)\n    alias(libs.plugins.diffplug.spotless)\n    alias(libs.plugins.gradleup.shadow)\n}\n\nrepositories {\n    mavenCentral()\n    maven {\n        url = 'https://maven.ecc.no/releases'\n    }\n    maven {\n        url = 'https://repo.osgeo.org/repository/release/'\n        content {\n            includeGroup 'org.geotools'\n            includeGroup 'org.geotools.ogc'\n            includeGroup 'org.eclipse.imagen'\n            includeGroup 'it.geosolutions.jgridshift'\n        }\n    }\n}\n\ndependencies {\n    // Core library dependency\n    implementation project(':mlt-core')\n\n    // CLI-specific dependencies\n    implementation(libs.commons.cli)\n    implementation(libs.commons.compress)\n\n    implementation(libs.mbtiles4j)\n    implementation(libs.sqlite.jdbc)\n\n    // Additional dependencies needed by CLI classes\n    implementation(libs.gson)\n    implementation(libs.protobuf.java)\n    implementation(libs.jakarta.annotation.api)\n    implementation(libs.jetbrains.annotations)\n    implementation(libs.jts.core)\n    implementation(libs.jts.io.common)\n\n    implementation(libs.log4j.api)\n    implementation(libs.log4j.slf4j.impl)\n    runtimeOnly(libs.jackson.databind)  // Required for JSON configuration of log4j2\n\n    // Kotlin\n    implementation(libs.kotlin.stdlib)\n\n    // pmtiles support\n    implementation(libs.planetiler.core)\n    implementation(platform(libs.tileverse.io))\n    implementation(libs.tileverse.rangereader.all)\n\n    testImplementation(libs.junit.jupiter.api)\n    testRuntimeOnly(libs.junit.jupiter.engine)\n    testRuntimeOnly(libs.junit.jupiter.platform.launcher)\n    testImplementation(libs.junit.jupiter.params)\n\n    testImplementation 'org.mockito:mockito-core:5.2.0'\n    testImplementation 'org.mockito:mockito-inline:5.2.0'\n    testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.0'\n}\n\n// Exclude slf4j-simple from all configurations to prevent conflicts with log4j-slf4j-impl\nconfigurations.all {\n    exclude group: 'org.slf4j', module: 'slf4j-simple' // no control over log output\n    exclude group: 'org.slf4j', module: 'log4j-over-slf4j' // only for log4j 1.x\n}\n\ntest {\n    useJUnitPlatform()\n    testLogging {\n        outputs.upToDateWhen { false }\n        showStandardStreams = true\n        events \"passed\", \"skipped\", \"failed\"\n        exceptionFormat = \"full\"\n    }\n    finalizedBy jacocoTestReport\n}\n\njacocoTestReport {\n    reports {\n        xml.required = true\n        html.required = false\n    }\n    dependsOn test\n}\n\njava {\n    toolchain {\n        languageVersion = JavaLanguageVersion.of(21)\n    }\n}\n\nkotlin {\n    jvmToolchain { languageVersion.set(JavaLanguageVersion.of(21)) }\n}\n\nspotless {\n    groovy {\n        target 'build.gradle'\n        leadingTabsToSpaces(4)\n        trimTrailingWhitespace()\n        endWithNewline()\n\n    }\n    kotlin {\n        target 'src/**/*.kt'\n        ktlint()\n    }\n    java {\n        importOrder()\n        target 'src/*/java/**/*.java'\n        googleJavaFormat()\n        removeUnusedImports()\n    }\n}\n\ngradle.projectsEvaluated {\n    tasks.withType(JavaCompile).tap {\n        configureEach {\n            options.compilerArgs << \"-Xlint:unchecked\"\n            options.compilerArgs << \"-Xlint:deprecation\"\n            options.compilerArgs << \"-Xlint:all\"\n        }\n    }\n}\n\ntasks.withType(Test).configureEach {\n    useJUnitPlatform()\n}\n\n// Create CLI JARs\n[\"encode\", \"decode\"].each { name ->\n    task \"$name\"(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar, dependsOn: [compileJava, ':mlt-core:jar']) {\n        archiveFileName = name + \".jar\"\n        archiveClassifier = \"cli\"\n        duplicatesStrategy = DuplicatesStrategy.EXCLUDE\n        configurations = project.configurations.named('runtimeClasspath').map { [it] }\n\n        // Exclude the log config file from some dependency and include our own.\n        // This only works because they use different extensions, else the exclude would remove both.\n        exclude 'log4j2.properties'\n        from('src/main/resources') {\n            include 'log4j2.json'\n            duplicatesStrategy = DuplicatesStrategy.WARN\n        }\n\n        // Filter out dependency JAR signatures to prevent\n        // `SecurityException: Invalid signature file digest for Manifest main attributes`\n        exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'\n\n        // Tileverse relies on the contents of META-INF/services to register service providers.\n        // By default, these are ignored when building a fat JAR, so we need to merge them.\n        filesMatching('META-INF/services/**') {\n            duplicatesStrategy = DuplicatesStrategy.INCLUDE\n        }\n        // Log4j relies on the contents of META-INF/org/apache/logging/log4j/core/config/plugins to register plugins.\n        filesMatching('META-INF/org/apache/logging/log4j/core/config/plugins/**') {\n            duplicatesStrategy = DuplicatesStrategy.INCLUDE\n        }\n        mergeServiceFiles()\n\n        manifest.from jar.manifest\n        with jar\n    }\n}\n\nencode.manifest.attributes 'Implementation-Title': 'encode an mlt from an mvt',\n        'Main-Class': 'org.maplibre.mlt.cli.Encode'\ndecode.manifest.attributes 'Implementation-Title': 'decode an mlt',\n        'Main-Class': 'org.maplibre.mlt.cli.Decode'\n\ntasks.register('cli') {\n    // Uncomment the following line to clear out the jars before rebuilding\n    delete \"${layout.buildDirectory}/libs\"\n    dependsOn encode\n    dependsOn decode\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Conversion.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport com.onthegomap.planetiler.geo.TileCoord\nimport org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream\nimport org.apache.commons.compress.compressors.deflate.DeflateCompressorOutputStream\nimport org.apache.commons.compress.compressors.deflate.DeflateParameters\nimport org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream\nimport org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream\nimport org.apache.commons.compress.compressors.gzip.GzipParameters\nimport org.apache.commons.lang3.mutable.MutableBoolean\nimport org.maplibre.mlt.compare.CompareHelper\nimport org.maplibre.mlt.compare.CompareHelper.CompareMode\nimport org.maplibre.mlt.converter.ColumnMapping\nimport org.maplibre.mlt.converter.ConversionConfig\nimport org.maplibre.mlt.converter.FeatureTableOptimizations\nimport org.maplibre.mlt.converter.MltConverter\nimport org.maplibre.mlt.converter.mvt.MvtUtils\nimport org.maplibre.mlt.decoder.MltDecoder\nimport org.maplibre.mlt.metadata.tileset.MltMetadata\nimport java.io.BufferedInputStream\nimport java.io.ByteArrayOutputStream\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.sql.Connection\nimport java.sql.SQLException\nimport java.util.Optional\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.atomic.AtomicLong\nimport java.util.stream.Collectors\nimport java.util.zip.Inflater\nimport java.util.zip.InflaterInputStream\n\nfun decompress(srcStream: InputStream): ByteArray {\n    var decompressInputStream: InputStream? = null\n    // Check for common compression formats by looking at the header bytes\n    // Buffered stream is not closed here because it would also close the underlying stream\n    val readStream = BufferedInputStream(srcStream)\n    if (readStream.available() > 3) {\n        readStream.mark(4)\n        val header = readStream.readNBytes(4)\n        readStream.reset()\n\n        if (DeflateCompressorInputStream.matches(header, header.size)) {\n            // deflate with zlib header\n            val inflater = Inflater(false)\n            decompressInputStream = InflaterInputStream(readStream, inflater)\n        } else if (header[0].toInt() == 0x1f && header[1] == 0x8b.toByte()) {\n            // TODO: why doesn't GZIPInputStream work here?\n            // decompressInputStream = new GZIPInputStream(readStream);\n            decompressInputStream = GzipCompressorInputStream(readStream)\n        }\n    }\n\n    if (decompressInputStream != null) {\n        ByteArrayOutputStream().use { outputStream ->\n            decompressInputStream.transferTo(outputStream)\n            return outputStream.toByteArray()\n        }\n    }\n\n    return readStream.readAllBytes()\n}\n\nfun createCompressStream(\n    src: OutputStream,\n    compressionType: String?,\n): OutputStream {\n    if (compressionType == \"gzip\") {\n        val parameters = GzipParameters()\n        parameters.setCompressionLevel(9)\n        return GzipCompressorOutputStream(src, parameters)\n    }\n    if (compressionType == \"deflate\") {\n        val parameters = DeflateParameters()\n        parameters.setCompressionLevel(9)\n        parameters.setWithZlibHeader(false)\n        return DeflateCompressorOutputStream(src, parameters)\n    }\n    return src\n}\n\nfun vacuumDatabase(connection: Connection): Boolean {\n    logger.debug(\"Optimizing database\")\n    try {\n        connection.createStatement().use { stmt ->\n            stmt.execute(\"VACUUM\")\n            return true\n        }\n    } catch (ex: SQLException) {\n        logger.error(\"Failed to optimize database\", ex)\n    }\n    return false\n}\n\nfun getTileLabel(\n    x: Long,\n    y: Long,\n    z: Int,\n): String = String.format(\"%d:%d,%d\", z, x, y)\n\nfun getTileLabel(c: TileCoord): String = getTileLabel(c.x.toLong(), c.y.toLong(), c.z)\n\nfun getTileLabels(\n    coords: Iterable<TileCoord>,\n    limit: Int,\n): String =\n    coords.joinToString(\n        separator = \", \",\n        limit = limit,\n        truncated = \"...\",\n        transform = ::getTileLabel,\n    )\n\nfun logColumnMappings(\n    x: Long,\n    y: Long,\n    z: Int,\n    metadata: MltMetadata.TileSetMetadata,\n) {\n    if (!logger.isTraceEnabled) {\n        return\n    }\n\n    for (table in metadata.featureTables) {\n        for (column in table.columns) {\n            val complex = column.field().type().complexType()\n            if (complex != null &&\n                complex.physicalType == MltMetadata.ComplexType.STRUCT\n            ) {\n                val mappings =\n                    complex.children\n                        .map { child -> (column.getName() ?: \"\") + child.name }\n                        .toSortedSet()\n                        .joinToString(\", \")\n                val added = MutableBoolean(false)\n                val entry =\n                    loggedColumnMappings.computeIfAbsent(mappings, { _ ->\n                        added.setTrue()\n                        loggedColumnMappingId.incrementAndGet()\n                    })\n\n                if (added.isTrue) {\n                    logger.trace(colMapMarker, \"{}:{},{} Found new column mapping {} for {}: {}\", z, x, y, entry, table.name, mappings)\n                } else {\n                    logger.trace(colMapMarker, \"{}:{},{} Found existing column mapping for {}: {}\", z, x, y, table.name, entry)\n                }\n            }\n        }\n    }\n}\n\n/**\n * Converts a tile from MVT to MLT, handling de- and re-compression\n *\n * @param x The x-coordinate of the tile.\n * @param y The y-coordinate of the tile.\n * @param z The zoom level of the tile.\n * @param srcTileData The source tile data as a byte array.\n * @param config The configuration for the conversion process\n * @param compressionRatioThreshold An optional threshold for the compression ratio to determine\n * whether compression is worth the need to decompress it. The compressed version will only be\n * used if its size is less than the original size multiplied by this ratio. If not present, a\n * compressed result will be used even if it's larger than the original.\n * @param compressionFixedThreshold An optional fixed byte threshold to determine whether\n * compression is worth the need to decompress it. The compressed version will only be used if\n * its size is at least this many bytes smaller than the original size. If not present, a\n * compressed result will be used even if it's larger than the original.\n * @param didCompress A mutable boolean to indicate whether compression was applied\n * @return The converted tile data as a byte array, or null if the conversion failed.\n */\nfun convertTile(\n    x: Long,\n    y: Long,\n    z: Int,\n    srcTileData: ByteArray,\n    config: EncodeConfig,\n    compressionRatioThreshold: Optional<Double>,\n    compressionFixedThreshold: Optional<Long>,\n    didCompress: MutableBoolean?,\n): ByteArray? {\n    try {\n        // Decode the source tile data into an intermediate representation.\n        val decodedMvTile = MvtUtils.decodeMvt(srcTileData)\n\n        val isIdPresent = true\n        val metadata =\n            MltConverter.createTilesetMetadata(\n                decodedMvTile,\n                config.conversionConfig,\n                config.columnMappingConfig,\n                isIdPresent,\n            )\n\n        // Print column mappings if verbosity level is high.\n        logColumnMappings(x, y, z, metadata)\n\n        // Apply column mappings and update the conversion configuration.\n        val targetConfig = applyColumnMappingsToConversionConfig(config, metadata)\n\n        // Convert the tile using the updated configuration and tessellation source.\n        var tileData =\n            MltConverter.encode(\n                decodedMvTile,\n                metadata,\n                targetConfig,\n                config.tessellateSource,\n            )\n\n        // Apply compression if specified.\n        val uncompressedTileData = tileData\n        if (config.compressionType != null) {\n            ByteArrayOutputStream().use { outputStream ->\n                createCompressStream(\n                    outputStream,\n                    config.compressionType,\n                ).use { compressStream ->\n                    compressStream.write(tileData)\n                }\n                // Evaluate whether the compressed version is worth using.\n                if ((\n                        compressionFixedThreshold.isEmpty ||\n                            outputStream.size() <= tileData.size - compressionFixedThreshold.get()\n                    ) &&\n                    (\n                        compressionRatioThreshold.isEmpty ||\n                            outputStream.size() <= tileData.size * compressionRatioThreshold.get()\n                    )\n                ) {\n                    if (logger.isTraceEnabled) {\n                        val compressedSize = outputStream.size()\n                        val originalSize = tileData.size\n                        logger.trace(\n                            compressMarker,\n                            \"{} compressed {} to {} bytes ({}%)\",\n                            getTileLabel(x, y, z),\n                            originalSize,\n                            compressedSize,\n                            String.format(\n                                \"%.1f\",\n                                100.0 * compressedSize / originalSize,\n                            ),\n                        )\n                    }\n                    totalCompressedInput.addAndGet(tileData.size.toLong())\n                    totalCompressedOutput.addAndGet(outputStream.size().toLong())\n                    totalCompressedTiles.incrementAndGet()\n                    if (didCompress != null) {\n                        didCompress.setTrue()\n                    }\n                    tileData = outputStream.toByteArray()\n                } else {\n                    if (logger.isTraceEnabled) {\n                        val compressedSize = outputStream.size()\n                        val originalSize = tileData.size\n                        val pctStr =\n                            String.format(\n                                \"%.1f\",\n                                100.0 * compressedSize / originalSize,\n                            )\n                        logger.trace(\n                            compressMarker,\n                            \"Compression of {} not effective ({} vs {} bytes, {}%), using uncompressed\",\n                            getTileLabel(x, y, z),\n                            originalSize,\n                            compressedSize,\n                            pctStr,\n                        )\n                    }\n                    totalUncompressedTiles.incrementAndGet()\n                    if (didCompress != null) {\n                        didCompress.setFalse()\n                    }\n                }\n            }\n        }\n        if (config.compareMode != CompareMode.None) {\n            logger.trace(\"Decoding converted tile {}:{},{} for comparison\", z, x, y)\n            val decodedTile = MltDecoder.decodeMlTile(uncompressedTileData)\n            val difference =\n                CompareHelper.compareTiles(\n                    decodedTile,\n                    decodedMvTile,\n                    config.compareMode,\n                    targetConfig.layerFilterPattern(),\n                    targetConfig.layerFilterInvert(),\n                )\n            if (difference.isPresent) {\n                logger.warn(\"Decoded tile {}:{},{} doesn't match: {}\", z, x, y, difference)\n            } else {\n                logger.trace(\"Tiles match: {}:{},{}\", z, x, y)\n            }\n        }\n        return tileData\n    } catch (ex: IOException) {\n        // Log an error message if tile conversion fails.\n        logger.error(\"Failed to process tile {}:{},{}\", z, x, y, ex)\n    }\n    return null\n}\n\n/** In batch conversion, we don't have the column names yet when we set up the config, so\nwe need to apply the mappings to an existing immutable config object using the column\nnames from a decoded tile metadata. */\nfun applyColumnMappingsToConversionConfig(\n    config: EncodeConfig,\n    metadata: MltMetadata.TileSetMetadata,\n): ConversionConfig {\n    val conversionConfig = config.conversionConfig\n    // If the config already has optimizations, don't modify it\n    if (!conversionConfig.optimizations().isEmpty()) {\n        return conversionConfig\n    }\n\n    // Warn if both patterns match on any tables\n    val warnTables =\n        metadata.featureTables\n            .stream()\n            .filter { table: MltMetadata.FeatureTable? ->\n                config.sortFeaturesPattern != null &&\n                    config.sortFeaturesPattern.matcher(table!!.name).matches()\n            }.filter { table: MltMetadata.FeatureTable? ->\n                config.regenIDsPattern != null &&\n                    config.regenIDsPattern.matcher(table!!.name).matches()\n            }.map<String?> { table: MltMetadata.FeatureTable? -> table!!.name }\n            .collect(Collectors.joining(\",\"))\n    if (!warnTables.isEmpty()) {\n        logger.warn(\n            \"The --{} and --{} options are incompatible: {}\",\n            EncodeCommandLine.SORT_FEATURES_OPTION,\n            EncodeCommandLine.REGEN_IDS_OPTION,\n            warnTables,\n        )\n    }\n\n    // Now that we have the actual column names from the metadata, we can determine which column\n    // mappings apply to which tables and create the optimizations for each table accordingly.\n    val optimizationMap =\n        metadata.featureTables\n            .stream()\n            .collect(\n                Collectors.toUnmodifiableMap(\n                    { table: MltMetadata.FeatureTable -> table.name },\n                    { table: MltMetadata.FeatureTable ->\n                        FeatureTableOptimizations(\n                            config.sortFeaturesPattern != null &&\n                                config.sortFeaturesPattern\n                                    .matcher(table.name)\n                                    .matches(),\n                            config.regenIDsPattern != null &&\n                                config.regenIDsPattern.matcher(table.name).matches(),\n                            config.columnMappingConfig.entries\n                                .stream()\n                                .filter { entry ->\n                                    entry\n                                        .key\n                                        .matcher(\n                                            table.name,\n                                        ).matches()\n                                }.flatMap<ColumnMapping> { entry ->\n                                    entry.value.stream()\n                                }.toList(),\n                        )\n                    },\n                ),\n            )\n\n    // re-create the config with the applied column mappings\n    return conversionConfig.toBuilder().optimizations(optimizationMap).build()\n}\n\nval totalCompressedInput = AtomicLong(0L)\nval totalCompressedOutput = AtomicLong(0L)\nval totalCompressedTiles = AtomicLong(0L)\nval totalUncompressedTiles = AtomicLong(0L)\n\nval loggedColumnMappingId = AtomicLong(0L)\nval loggedColumnMappings = ConcurrentHashMap<String, Long>()\n\nconst val MBTILES_METADATA_MIME_TYPE = \"application/vnd.maplibre-tile\"\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Decode.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.commons.cli.CommandLine\nimport org.apache.commons.cli.DefaultParser\nimport org.apache.commons.cli.Option\nimport org.apache.commons.cli.Options\nimport org.apache.commons.cli.ParseException\nimport org.apache.commons.cli.help.HelpFormatter\nimport org.maplibre.mlt.data.MapLibreTile\nimport org.maplibre.mlt.decoder.MltDecoder\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Files\nimport java.nio.file.Paths\n\nobject Decode {\n    private const val FILE_NAME_ARG = \"mlt\"\n    private const val PRINT_MLT_OPTION = \"printmlt\"\n    private const val TIMER_OPTION = \"timer\"\n\n    @JvmStatic\n    fun main(args: Array<String>) {\n        try {\n            run(args)\n        } catch (ex: Exception) {\n            logger.error(\"Decoding failed\", ex)\n            System.exit(1)\n        }\n    }\n\n    private fun run(args: Array<String>) {\n        val options = Options()\n        options.addOption(\n            Option\n                .builder()\n                .longOpt(FILE_NAME_ARG)\n                .hasArg(true)\n                .desc(\"Path to the input MLT file to read ([REQUIRED])\")\n                .required(true)\n                .get(),\n        )\n        options.addOption(\n            Option\n                .builder()\n                .longOpt(PRINT_MLT_OPTION)\n                .hasArg(false)\n                .desc(\"Print the MLT tile after encoding it ([OPTIONAL], default: false)\")\n                .required(false)\n                .get(),\n        )\n        options.addOption(\n            Option\n                .builder()\n                .longOpt(TIMER_OPTION)\n                .hasArg(true)\n                .optionalArg(true)\n                .argName(\"count\")\n                .desc(\"Print the time it takes, in ms, to decode a tile <count> times ([OPTIONAL], default 1)\")\n                .required(false)\n                .get(),\n        )\n\n        if (args.isEmpty()) {\n            showHelp(options)\n            System.exit(1)\n        }\n        try {\n            val cmd = DefaultParser().parse(options, args)\n            run(cmd)\n        } catch (ex: ParseException) {\n            System.err.println(ex.message)\n            showHelp(options)\n            throw ex\n        } catch (ex: Exception) {\n            System.err.println(\"Failed: \" + ex.message)\n            ex.printStackTrace(System.err)\n            throw ex\n        }\n    }\n\n    private fun run(cmd: CommandLine) {\n        val fileName = cmd.getOptionValue(FILE_NAME_ARG)\n        if (fileName == null || fileName.isEmpty()) {\n            throw ParseException(\"Missing required argument: \" + FILE_NAME_ARG)\n        }\n        val willPrintMLT = cmd.hasOption(PRINT_MLT_OPTION)\n        val willTime = cmd.hasOption(TIMER_OPTION)\n        val decodeIterations = cmd.getOptionValue(TIMER_OPTION, \"1\").toInt().coerceAtLeast(1)\n        val inputTilePath = Paths.get(fileName)\n        require(Files.exists(inputTilePath)) { \"Input mlt tile path does not exist: \" + inputTilePath }\n        val mltTileBuffer = Files.readAllBytes(inputTilePath)\n\n        val timer = Timer()\n        var decodedTile: MapLibreTile? = null\n        for (i in 0 until decodeIterations) {\n            decodedTile = MltDecoder.decodeMlTile(mltTileBuffer)\n        }\n        if (willTime) timer.stop(\"decoding\")\n        if (willPrintMLT && decodedTile != null) {\n            System.out.write(decodedTile.toJson().toByteArray(StandardCharsets.UTF_8))\n        }\n    }\n\n    private fun showHelp(options: Options) {\n        HelpFormatter\n            .builder()\n            .setShowSince(false)\n            .get()\n            .printHelp(Decode::class.java.name, \"\", options, null, true)\n    }\n\n    private val logger: Logger = LoggerFactory.getLogger(Decode::class.java)\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Encode.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.commons.cli.CommandLine\nimport org.apache.commons.io.FilenameUtils\nimport org.apache.logging.log4j.Level\nimport org.apache.logging.log4j.core.config.Configurator\nimport org.maplibre.mlt.cli.EncodeCommandLine.getColumnMappings\nimport org.maplibre.mlt.compare.CompareHelper\nimport org.maplibre.mlt.compare.CompareHelper.CompareMode\nimport org.maplibre.mlt.converter.ConversionConfig\nimport org.maplibre.mlt.converter.MltConverter\nimport org.maplibre.mlt.converter.encodings.fsst.FsstEncoder\nimport org.maplibre.mlt.converter.encodings.fsst.FsstJni\nimport org.maplibre.mlt.converter.mvt.MvtUtils\nimport org.maplibre.mlt.decoder.MltDecoder\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport java.io.ByteArrayInputStream\nimport java.io.File\nimport java.io.IOException\nimport java.net.URI\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Files\nimport java.nio.file.InvalidPathException\nimport java.nio.file.Path\nimport java.nio.file.Paths\n\nobject Encode {\n    @JvmStatic\n    fun main(args: Array<String>) {\n        if (!run(args)) {\n            System.exit(1)\n        }\n    }\n\n    fun run(args: Array<String>): Boolean {\n        try {\n            val cmd = EncodeCommandLine.getCommandLine(args)\n            if (cmd == null) {\n                return false\n            }\n\n            if (cmd.hasOption(EncodeCommandLine.SERVER_ARG)) {\n                val port = cmd.getOptionValue(EncodeCommandLine.SERVER_ARG, \"3001\").toInt()\n                // never returns\n                Server().run(port)\n            }\n\n            return run(cmd)\n        } catch (ex: Exception) {\n            logger.error(\"Failed\", ex)\n            return false\n        }\n    }\n\n    private fun run(cmd: CommandLine): Boolean {\n        val tileFileNames = cmd.getOptionValues(EncodeCommandLine.INPUT_TILE_ARG)\n        val sortFeaturesPattern = cmd.sortFeaturesPattern\n        val regenIDsPattern = cmd.regenIDsPattern\n        val outlineFeatureTables = cmd.outlineFeatureTables\n        val useFSSTJava = cmd.useFSSTJava\n        val useFSSTNative = cmd.useFSSTNative\n        val tessellateURI = cmd.tessellateSource?.let { URI(it) }\n        val tessellatePolygons = cmd.tessellatePolygons\n        val compressionType = cmd.compressionType\n        val filterPattern = cmd.filterPattern\n        val filterInvert = cmd.filterInvert\n        val minZoom = cmd.minZoom\n        val maxZoom = cmd.maxZoom\n        val logLevel = cmd.logLevel\n        val threadCount = cmd.threadCount\n\n        Configurator.setRootLevel(logLevel)\n\n        // PMTiles logs stats at INFO.  Enable that only if the user has selected at least debug.\n        // Note: `isLessSpecificThan` is actually less-than-or-equal.\n        Configurator.setLevel(\n            \"com.onthegomap.planetiler.pmtiles.WriteablePmtiles\",\n            if (logLevel.isLessSpecificThan(Level.DEBUG)) Level.INFO else Level.OFF,\n        )\n\n        val columnMappings = getColumnMappings(cmd)\n\n        val taskRunner = createBoundedNonRejectingTaskRunner(threadCount)\n        logger.debug(\"Using {} thread(s)\", taskRunner.threadCount + 1)\n\n        logger.debug(\n            \"Using column mappings: {}\",\n            if (columnMappings.isEmpty()) \"none\" else columnMappings.toString(),\n        )\n\n        var useFSST = false\n        if (useFSSTNative) {\n            if (FsstJni.isLoaded() && FsstEncoder.useNative(true)) {\n                useFSST = true\n            } else {\n                logger.warn(\"Native FSST could not be loaded\", FsstJni.getLoadError())\n            }\n        } else if (useFSSTJava) {\n            logger.debug(\"Using Java FSST encoder\")\n            FsstEncoder.useNative(false)\n            useFSST = true\n        }\n\n        val typeMismatchPolicy =\n            if (cmd.enableCoerceOnTypeMismatch) {\n                ConversionConfig.TypeMismatchPolicy.COERCE\n            } else if (cmd.enableElideOnTypeMismatch) {\n                ConversionConfig.TypeMismatchPolicy.ELIDE\n            } else {\n                ConversionConfig.TypeMismatchPolicy.FAIL\n            }\n\n        val conversionConfig =\n            ConversionConfig\n                .builder()\n                .includeIds(!cmd.hasOption(EncodeCommandLine.EXCLUDE_IDS_OPTION))\n                .useFastPFOR(cmd.hasOption(EncodeCommandLine.FASTPFOR_ENCODING_OPTION))\n                .useFSST(useFSST)\n                .typeMismatchPolicy(typeMismatchPolicy)\n                .preTessellatePolygons(tessellatePolygons)\n                .useMortonEncoding(!cmd.hasOption(EncodeCommandLine.NO_MORTON_OPTION))\n                .outlineFeatureTableNames(\n                    if (outlineFeatureTables != null) outlineFeatureTables.toList() else listOf<String>(),\n                ).layerFilterPattern(filterPattern)\n                .layerFilterInvert(filterInvert)\n                .integerEncodingOption(ConversionConfig.IntegerEncodingOption.AUTO)\n                .build()\n        if (outlineFeatureTables != null && outlineFeatureTables.size > 0) {\n            logger.debug(\n                \"Including outlines for layers: {}\",\n                outlineFeatureTables.joinToString(\", \"),\n            )\n        }\n\n        val encodeConfig =\n            EncodeConfig(\n                columnMappingConfig = columnMappings,\n                conversionConfig = conversionConfig,\n                tessellateSource = tessellateURI,\n                sortFeaturesPattern = sortFeaturesPattern,\n                regenIDsPattern = regenIDsPattern,\n                compressionType = compressionType,\n                minZoom = minZoom,\n                maxZoom = maxZoom,\n                willOutput =\n                    cmd.hasOption(EncodeCommandLine.OUTPUT_FILE_ARG) ||\n                        cmd.hasOption(EncodeCommandLine.OUTPUT_DIR_ARG),\n                willDecode = cmd.hasOption(EncodeCommandLine.DECODE_OPTION),\n                willPrintMLT = cmd.hasOption(EncodeCommandLine.PRINT_MLT_OPTION),\n                willPrintMVT = cmd.hasOption(EncodeCommandLine.PRINT_MVT_OPTION),\n                compareProp =\n                    cmd.hasOption(EncodeCommandLine.COMPARE_PROP_OPTION) ||\n                        cmd.hasOption(EncodeCommandLine.COMPARE_ALL_OPTION),\n                compareGeom =\n                    cmd.hasOption(EncodeCommandLine.COMPARE_GEOM_OPTION) ||\n                        cmd.hasOption(EncodeCommandLine.COMPARE_ALL_OPTION),\n                willTime = cmd.hasOption(EncodeCommandLine.TIMER_OPTION),\n                taskRunner = taskRunner,\n                continueOnError = cmd.hasOption(EncodeCommandLine.CONTINUE_OPTION),\n                logCacheStats = cmd.hasOption(EncodeCommandLine.CACHE_STATS_OPTION),\n            )\n\n        if (tileFileNames != null && tileFileNames.size > 0) {\n            require(!(tileFileNames.size > 1 && cmd.hasOption(EncodeCommandLine.OUTPUT_FILE_ARG))) {\n                (\n                    \"Multiple input files not allowed with single output file, use --\" +\n                        EncodeCommandLine.OUTPUT_DIR_ARG\n                )\n            }\n            for (tileFileName in tileFileNames) {\n                val outputPath = getOutputPath(cmd, tileFileName, \"mlt\")\n                if (outputPath == null) {\n                    continue\n                }\n\n                logger.debug(\"Converting {} to {}\", tileFileName, outputPath)\n                encodeTile(0, 0, 0, tileFileName, outputPath, encodeConfig)\n            }\n        } else if (cmd.hasOption(EncodeCommandLine.INPUT_MBTILES_ARG)) {\n            // Converting all the tiles in an MBTiles file\n            val inputPath = cmd.getOptionValue(EncodeCommandLine.INPUT_MBTILES_ARG)\n            val outputPath = getOutputPath(cmd, inputPath, \"mlt.mbtiles\")\n            if (!encodeMBTiles(inputPath, outputPath, encodeConfig)) {\n                return false\n            }\n        } else if (cmd.hasOption(EncodeCommandLine.INPUT_OFFLINEDB_ARG)) {\n            val inputPath = cmd.getOptionValue(EncodeCommandLine.INPUT_OFFLINEDB_ARG)\n            var ext = FilenameUtils.getExtension(inputPath)\n            if (ext != null && !ext.isEmpty()) {\n                ext = \".\" + ext\n            }\n            val outputPath = getOutputPath(cmd, inputPath, \"mlt\" + ext)\n            if (!encodeOfflineDB(Path.of(inputPath), outputPath, encodeConfig)) {\n                return false\n            }\n        } else if (cmd.hasOption(EncodeCommandLine.INPUT_PMTILES_ARG)) {\n            val inputPath = cmd.getOptionValue(EncodeCommandLine.INPUT_PMTILES_ARG)\n            var ext = FilenameUtils.getExtension(inputPath)\n            if (ext != null && !ext.isEmpty()) {\n                ext = \".\" + ext\n            }\n            var outputPath = getOutputPath(cmd, inputPath, \"mlt\" + ext)\n            if (outputPath == null) {\n                return false\n            }\n\n            val inputURI = getInputURI(inputPath)\n\n            outputPath = outputPath.toAbsolutePath()\n            if (!encodePMTiles(inputURI, outputPath, encodeConfig)) {\n                return false\n            }\n        }\n\n        if (logger.isDebugEnabled) {\n            val input = totalCompressedInput.get()\n            val output = totalCompressedOutput.get()\n            val compressed = totalCompressedTiles.get()\n            val uncompressed = totalUncompressedTiles.get()\n            val total = compressed + uncompressed\n            val sizePercentStr = if (input > 0) String.format(\" (%.1f%%)\", 100.0 * output / input) else \"\"\n            val countPercentStr = if (total > 0) String.format(\" (%.1f%%)\", 100.0 * compressed / total) else \"\"\n            logger.debug(\n                \"Compressed {} bytes to {} bytes{} in {} of {}{} tiles\",\n                input,\n                output,\n                sizePercentStr,\n                compressed,\n                total,\n                countPercentStr,\n            )\n        }\n        return true\n    }\n\n    /**  Convert a single tile from an individual file */\n    private fun encodeTile(\n        x: Long,\n        y: Long,\n        z: Int,\n        tileFileName: String,\n        outputPath: Path,\n        config: EncodeConfig,\n    ) {\n        val willCompare = config.compareMode != CompareMode.None\n        val inputTilePath = Paths.get(tileFileName)\n        val tileData =\n            Files.readAllBytes(inputTilePath).let {\n                try {\n                    decompress(ByteArrayInputStream(it))\n                } catch (ex: IOException) {\n                    logger.error(\"Failed to decompress tile {}\", tileFileName, ex)\n                    return\n                }\n            }\n\n        val decodedMvTile = MvtUtils.decodeMvt(tileData)\n\n        val willTime = config.willTime\n        val timer = if (willTime) Timer() else null\n\n        val isIdPresent = true\n        val metadata =\n            MltConverter.createTilesetMetadata(\n                decodedMvTile,\n                config.conversionConfig,\n                config.columnMappingConfig,\n                isIdPresent,\n            )\n\n        logColumnMappings(x, y, z, metadata)\n\n        val targetConfig = applyColumnMappingsToConversionConfig(config, metadata)\n\n        val mlTile =\n            MltConverter.encode(\n                decodedMvTile,\n                metadata,\n                targetConfig,\n                config.tessellateSource,\n            )\n        timer?.stop(\"encoding\")\n\n        if (config.willOutput) {\n            logger.debug(\"Writing converted tile to {}\", outputPath)\n\n            try {\n                Files.write(outputPath, mlTile)\n            } catch (ex: IOException) {\n                logger.error(\"Failed to write tile to {}\", outputPath, ex)\n            }\n        }\n        if (config.willPrintMVT) {\n            System.out.write(decodedMvTile.toJson().toByteArray(StandardCharsets.UTF_8))\n        }\n        val needsDecoding = config.willDecode || willCompare || config.willPrintMLT\n        if (needsDecoding) {\n            logger.debug(\"Decoding converted tile...\")\n            timer?.restart()\n\n            val decodedTile = MltDecoder.decodeMlTile(mlTile)\n            timer?.stop(\"decoding\")\n            if (config.willPrintMLT) {\n                System.out.write(decodedTile.toJson().toByteArray(StandardCharsets.UTF_8))\n            }\n            if (willCompare) {\n                val difference =\n                    CompareHelper.compareTiles(\n                        decodedTile,\n                        decodedMvTile,\n                        config.compareMode,\n                        targetConfig.layerFilterPattern(),\n                        targetConfig.layerFilterInvert(),\n                    )\n                if (difference.isPresent) {\n                    logger.warn(\"Tiles do not match: {}\", difference)\n                } else {\n                    logger.debug(\"Tiles match: {}:{},{}\", z, x, y)\n                }\n            }\n        }\n    }\n\n    private fun getInputURI(inputArg: String): URI {\n        val file = File(inputArg)\n        return if (file.isFile) {\n            file.absoluteFile.toURI().normalize()\n        } else {\n            URI.create(\n                inputArg,\n            )\n        }\n    }\n\n    /** Resolve an output filename.\n     * If an output filename is specified directly, use it.\n     * If only an output directory is given, add the input filename and the specified extension.\n     * If neither a directory nor file name is given, returns null.  This is used for testing.\n     * If a path is returned and the directory doesn't already exist, it is created. */\n    private fun getOutputPath(\n        cmd: CommandLine,\n        inputFileName: String,\n        targetExt: String?,\n    ): Path? = getOutputPath(cmd, inputFileName, targetExt, false)\n\n    private fun getOutputPath(\n        cmd: CommandLine,\n        inputFileName: String,\n        targetExt: String?,\n        forceExt: Boolean,\n    ): Path? {\n        val ext =\n            if (!targetExt.isNullOrEmpty()) {\n                FilenameUtils.EXTENSION_SEPARATOR_STR + targetExt\n            } else {\n                \"\"\n            }\n        var outputPath: Path? = null\n        if (cmd.hasOption(EncodeCommandLine.OUTPUT_FILE_ARG)) {\n            outputPath = Paths.get(cmd.getOptionValue(EncodeCommandLine.OUTPUT_FILE_ARG))\n        } else {\n            val outputDir = cmd.getOptionValue(EncodeCommandLine.OUTPUT_DIR_ARG, \"./\")\n\n            // Get the file basename without extension.  The input may be a local path or a URI (for\n            // pmtiles)\n            val inputURI = getInputURI(inputFileName)\n            if (inputURI.path == null) {\n                logger.error(\"Unable to determine input filename from '{}'\", inputFileName)\n                return null\n            }\n\n            var baseName: String?\n            try {\n                val inputPath = Paths.get(inputURI.path)\n                baseName = FilenameUtils.getBaseName(inputPath.fileName.toString())\n            } catch (ignored: InvalidPathException) {\n                //  Windows can't handle getting the path part of a file URI\n                baseName = FilenameUtils.getBaseName(inputFileName)\n            }\n\n            outputPath = Paths.get(outputDir, baseName + ext)\n        }\n        if (outputPath != null) {\n            if (forceExt) {\n                outputPath = Path.of(FilenameUtils.removeExtension(outputPath.toString()) + ext)\n            }\n\n            val outputDir = outputPath.toAbsolutePath().parent\n            if (!Files.exists(outputDir)) {\n                try {\n                    Files.createDirectories(outputDir)\n                } catch (ex: IOException) {\n                    logger.error(\"Failed to create directory '{}'\", outputDir, ex)\n                    return null\n                }\n            }\n        }\n        return outputPath\n    }\n\n    private val logger: Logger = LoggerFactory.getLogger(Encode::class.java)\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/EncodeCommandLine.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.commons.cli.CommandLine\nimport org.apache.commons.cli.Converter\nimport org.apache.commons.cli.DefaultParser\nimport org.apache.commons.cli.Option\nimport org.apache.commons.cli.Options\nimport org.apache.commons.cli.ParseException\nimport org.apache.commons.cli.help.HelpFormatter\nimport org.apache.commons.cli.help.TextHelpAppendable\nimport org.apache.commons.lang3.NotImplementedException\nimport org.apache.commons.lang3.StringUtils\nimport org.apache.logging.log4j.Level\nimport org.maplibre.mlt.converter.ColumnMapping\nimport org.maplibre.mlt.converter.ColumnMappingConfig\nimport org.maplibre.mlt.converter.encodings.fsst.FsstJni\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport java.io.IOException\nimport java.net.URI\nimport java.net.URISyntaxException\nimport java.util.regex.Pattern\nimport java.util.regex.PatternSyntaxException\nimport java.util.stream.Collectors\nimport java.util.stream.Stream\n\ninternal object EncodeCommandLine {\n    const val INPUT_TILE_ARG = \"mvt\"\n    const val INPUT_MBTILES_ARG = \"mbtiles\"\n    const val INPUT_OFFLINEDB_ARG = \"offlinedb\"\n    const val INPUT_PMTILES_ARG = \"pmtiles\"\n    const val OUTPUT_DIR_ARG = \"dir\"\n    const val OUTPUT_FILE_ARG = \"mlt\"\n    const val EXCLUDE_IDS_OPTION = \"noids\"\n    const val SORT_FEATURES_OPTION = \"sort-ids\"\n    const val REGEN_IDS_OPTION = \"regen-ids\"\n    const val FILTER_LAYERS_OPTION = \"filter-layers\"\n    const val MIN_ZOOM_OPTION = \"minzoom\"\n    const val MAX_ZOOM_OPTION = \"maxzoom\"\n    const val FILTER_LAYERS_INVERT_OPTION = \"filter-layers-invert\"\n    const val INCLUDE_METADATA_OPTION = \"metadata\"\n    const val ALLOW_COERCE_OPTION = \"coerce-mismatch\"\n    const val ALLOW_ELISION_OPTION = \"elide-mismatch\"\n    const val FASTPFOR_ENCODING_OPTION = \"enable-fastpfor\"\n    const val FSST_ENCODING_OPTION = \"enable-fsst\"\n    const val FSST_NATIVE_ENCODING_OPTION = \"enable-fsst-native\"\n    const val COLUMN_MAPPING_AUTO_OPTION = \"colmap-auto\"\n    const val COLUMN_MAPPING_DELIM_OPTION = \"colmap-delim\"\n    const val COLUMN_MAPPING_LIST_OPTION = \"colmap-list\"\n    const val NO_MORTON_OPTION = \"nomorton\"\n    const val PRE_TESSELLATE_OPTION = \"tessellate\"\n    const val TESSELLATE_URL_OPTION = \"tessellateurl\"\n    const val OUTLINE_FEATURE_TABLES_OPTION = \"outlines\"\n    const val DECODE_OPTION = \"decode\"\n    const val PRINT_MLT_OPTION = \"printmlt\"\n    const val PRINT_MVT_OPTION = \"printmvt\"\n    const val COMPARE_ALL_OPTION = \"compare-all\"\n    const val COMPARE_GEOM_OPTION = \"compare-geometry\"\n    const val COMPARE_PROP_OPTION = \"compare-properties\"\n    const val VECTORIZED_OPTION = \"vectorized\"\n    const val TIMER_OPTION = \"timer\"\n    const val COMPRESS_OPTION = \"compress\"\n    private const val COMPRESS_OPTION_DEFLATE = \"deflate\"\n    private const val COMPRESS_OPTION_GZIP = \"gzip\"\n    private const val COMPRESS_OPTION_NONE = \"none\"\n    private const val COMPRESS_OPTION_BROTLI = \"brotli\"\n    private const val COMPRESS_OPTION_ZSTD = \"zstd\"\n    const val PARALLEL_OPTION = \"parallel\"\n    const val VERBOSE_OPTION = \"verbose\"\n    const val CACHE_STATS_OPTION = \"cache-stats\"\n    const val CONTINUE_OPTION = \"continue\"\n    const val HELP_OPTION = \"help\"\n    const val SERVER_ARG = \"server\"\n\n    internal fun getAllowedCompressions(cmd: CommandLine): Stream<String> {\n        val extra = if (cmd.hasOption(INPUT_PMTILES_ARG)) Stream.of(COMPRESS_OPTION_BROTLI, COMPRESS_OPTION_ZSTD) else Stream.of()\n        return Stream.concat(\n            Stream.of(\n                COMPRESS_OPTION_GZIP,\n                COMPRESS_OPTION_DEFLATE,\n                COMPRESS_OPTION_NONE,\n            ),\n            extra,\n        )\n    }\n\n    private fun validateCompression(\n        cmd: CommandLine,\n        option: String?,\n    ): Boolean = getAllowedCompressions(cmd).anyMatch { x -> x == option }\n\n    fun getCommandLine(args: Array<String>): CommandLine? {\n        try {\n            val options = Options()\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(INPUT_TILE_ARG)\n                    .hasArgs()\n                    .argName(\"file\")\n                    .desc(\"Path to the input MVT file\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(INPUT_MBTILES_ARG)\n                    .hasArg(true)\n                    .argName(\"file\")\n                    .desc(\"Path of the input MBTiles file.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(INPUT_PMTILES_ARG)\n                    .hasArg(true)\n                    .argName(\"fileOrUri\")\n                    .desc(\"Location of the input PMTiles file.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(INPUT_OFFLINEDB_ARG)\n                    .hasArg(true)\n                    .argName(\"file\")\n                    .desc(\"Path of the input offline database file.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(OUTPUT_DIR_ARG)\n                    .hasArg(true)\n                    .argName(\"dir\")\n                    .desc(\n                        \"Directory where the output is written, using the input file basename (OPTIONAL).\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(OUTPUT_FILE_ARG)\n                    .hasArg(true)\n                    .argName(\"file\")\n                    .desc(\n                        \"Filename where the output will be written. Overrides --\" + OUTPUT_DIR_ARG + \".\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(EXCLUDE_IDS_OPTION)\n                    .hasArg(false)\n                    .desc(\"Don't include feature IDs.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(SORT_FEATURES_OPTION)\n                    .hasArg(true)\n                    .optionalArg(true)\n                    .argName(\"pattern\")\n                    .desc(\n                        \"Reorder features of matching layers (default all) by ID, for optimal encoding of ID values.\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(REGEN_IDS_OPTION)\n                    .hasArg(true)\n                    .optionalArg(true)\n                    .argName(\"pattern\")\n                    .desc(\n                        \"Re-generate ID values of matching layers (default all).  \" +\n                            \"Sequential values are assigned for optimal encoding, when ID values have no special meaning.\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(FILTER_LAYERS_OPTION)\n                    .hasArg(true)\n                    .desc(\"Filter layers by regex\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(FILTER_LAYERS_INVERT_OPTION)\n                    .hasArg(false)\n                    .desc(\"Invert the result of --\" + FILTER_LAYERS_OPTION)\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(MIN_ZOOM_OPTION)\n                    .hasArg(true)\n                    .optionalArg(false)\n                    .argName(\"level\")\n                    .desc(\n                        \"Minimum zoom level to encode tiles.  Only applies with --\" +\n                            INPUT_MBTILES_ARG +\n                            \" or --\" +\n                            INPUT_PMTILES_ARG,\n                    ).required(false)\n                    .converter(Converter.NUMBER)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(MAX_ZOOM_OPTION)\n                    .hasArg(true)\n                    .optionalArg(false)\n                    .argName(\"level\")\n                    .desc(\n                        (\n                            \"Maximum zoom level to encode tiles.  Only applies with --\" +\n                                INPUT_MBTILES_ARG +\n                                \" or --\" +\n                                INPUT_PMTILES_ARG\n                        ),\n                    ).required(false)\n                    .converter(Converter.NUMBER)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(ALLOW_COERCE_OPTION)\n                    .hasArg(false)\n                    .desc(\"Allow coercion of property values\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(ALLOW_ELISION_OPTION)\n                    .hasArg(false)\n                    .desc(\"Allow elision of mismatched property types\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(FASTPFOR_ENCODING_OPTION)\n                    .hasArg(false)\n                    .desc(\"Enable FastPFOR encodings of integer columns\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(FSST_ENCODING_OPTION)\n                    .hasArg(false)\n                    .desc(\"Enable FSST encodings of string columns (Java implementation)\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(FSST_NATIVE_ENCODING_OPTION)\n                    .hasArg(false)\n                    .desc(\n                        \"Enable FSST encodings of string columns (Native implementation: \" +\n                            (if (FsstJni.isLoaded()) \"\" else \"Not \") +\n                            \" available)\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COLUMN_MAPPING_AUTO_OPTION)\n                    .hasArg(true)\n                    .optionalArg(false)\n                    .argName(\"columns\")\n                    .valueSeparator(',')\n                    .desc(\n                        \"\"\"\nAutomatic column mapping for the specified layers (Not implemented)\n  Layer specifications may be regular expressions if surrounded by '/'.\n  An empty set of layers applies to all layers.\n                        \"\"\".trimIndent(),\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COLUMN_MAPPING_DELIM_OPTION)\n                    .hasArg(true)\n                    .optionalArg(false)\n                    .argName(\"map\")\n                    .desc(\n                        \"\"\"\nAdd a separator-based column mapping:\n  '[<layer>,...]<name><separator>'\n  e.g. '[][:]name' combines 'name' and 'name:de' on all layers.\n                        \"\"\".trimIndent(),\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COLUMN_MAPPING_LIST_OPTION)\n                    .hasArg(true)\n                    .optionalArg(false)\n                    .argName(\"map\")\n                    .desc(\n                        \"\"\"\nAdd an explicit column mapping on the specified layers:\n  '[<layer>,...]<name>,...'\n  e.g. '[]name,name:de' combines 'name' and 'name:de' on all layers.\n                        \"\"\".trimIndent(),\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(NO_MORTON_OPTION)\n                    .hasArg(false)\n                    .desc(\"Disable Morton encoding.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(PRE_TESSELLATE_OPTION)\n                    .hasArg(false)\n                    .desc(\"Include tessellation data in converted tiles.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(TESSELLATE_URL_OPTION)\n                    .hasArg(true)\n                    .desc(\"Use a tessellation server (implies --\" + PRE_TESSELLATE_OPTION + \").\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(OUTLINE_FEATURE_TABLES_OPTION)\n                    .hasArgs()\n                    .desc(\n                        \"The feature tables for which outlines are included \" +\n                            \"([OPTIONAL], comma-separated, 'ALL' for all, default: none).\",\n                    ).valueSeparator(',')\n                    .argName(\"tables\")\n                    .required(false)\n                    .optionalArg(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(DECODE_OPTION)\n                    .hasArg(false)\n                    .desc(\n                        \"Test decoding the tile after encoding it. \" +\n                            \"Only applies with --\" +\n                            INPUT_TILE_ARG +\n                            \".\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(PRINT_MLT_OPTION)\n                    .hasArg(false)\n                    .desc(\n                        (\n                            \"Print the MLT tile to stdout after encoding it. \" +\n                                \"Only applies with --\" +\n                                INPUT_TILE_ARG +\n                                \".\"\n                        ),\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(PRINT_MVT_OPTION)\n                    .hasArg(false)\n                    .desc(\n                        \"Print the round-tripped MVT tile to stdout. \" +\n                            \"Only applies with --\" +\n                            INPUT_TILE_ARG +\n                            \".\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COMPARE_GEOM_OPTION)\n                    .hasArg(false)\n                    .desc(\"Assert that geometry in the decoded tile is the same as the input tile.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COMPARE_PROP_OPTION)\n                    .hasArg(false)\n                    .desc(\"Assert that properties in the decoded tile is the same as the input tile.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COMPARE_ALL_OPTION)\n                    .hasArg(false)\n                    .desc(\"Equivalent to --\" + COMPARE_GEOM_OPTION + \" --\" + COMPARE_PROP_OPTION + \".\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(VECTORIZED_OPTION)\n                    .hasArg(false)\n                    .desc(\"Use the vectorized decoding path.\")\n                    .required(false)\n                    .deprecated()\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(TIMER_OPTION)\n                    .hasArg(false)\n                    .desc(\"Print the time it takes, in ms, to decode a tile.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(COMPRESS_OPTION)\n                    .hasArg(true)\n                    .argName(\"algorithm\")\n                    .desc(\n                        \"Compress tile data with one of 'deflate', 'gzip', or 'none'. \" +\n                            \"Only applies with --\" +\n                            INPUT_MBTILES_ARG +\n                            \", --\" +\n                            INPUT_PMTILES_ARG +\n                            \" or --\" +\n                            INPUT_OFFLINEDB_ARG +\n                            \".\" +\n                            \" Default: none for MBTiles and offline database, keep existing for PMTiles.\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .option(\"j\")\n                    .longOpt(PARALLEL_OPTION)\n                    .hasArg(true)\n                    .optionalArg(true)\n                    .argName(\"threads\")\n                    .desc(\n                        \"Enable parallel encoding of tiles.  Only applies with --\" +\n                            INPUT_MBTILES_ARG +\n                            \", --\" +\n                            INPUT_OFFLINEDB_ARG +\n                            \", or --\" +\n                            INPUT_PMTILES_ARG,\n                    ).required(false)\n                    .converter(Converter.NUMBER)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(CONTINUE_OPTION)\n                    .hasArg(false)\n                    .desc(\"Continue on error when possible\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .option(\"v\")\n                    .longOpt(VERBOSE_OPTION)\n                    .hasArg(true)\n                    .optionalArg(true)\n                    .argName(\"level\")\n                    .desc(\n                        \"Select output verbosity for status and information on stderr. \" +\n                            \"Optionally specify a level: off, fatal, error, warn, info, debug, trace. \" +\n                            \"Default is info, or debug if --\" +\n                            VERBOSE_OPTION +\n                            \" is specified without a level.\",\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(CACHE_STATS_OPTION)\n                    .hasArg(false)\n                    .desc(\n                        \"Show cache stats. Implies verbose output. Only applies with --\" +\n                            INPUT_PMTILES_ARG,\n                    ).required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .option(\"h\")\n                    .longOpt(HELP_OPTION)\n                    .hasArg(false)\n                    .desc(\"Show this output.\")\n                    .required(false)\n                    .get(),\n            )\n            options.addOption(\n                Option\n                    .builder()\n                    .longOpt(SERVER_ARG)\n                    .hasArg(true)\n                    .optionalArg(true)\n                    .argName(\"port\")\n                    .desc(\"Start encoding server\")\n                    .required(false)\n                    .get(),\n            )\n\n            val cmd = DefaultParser().parse(options, args)\n\n            val tessellateSource = cmd.getOptionValue(TESSELLATE_URL_OPTION, null as String?)\n            if (tessellateSource != null) {\n                // throw if it's not a valid URI\n                URI(tessellateSource)\n            }\n\n            val filterRegex = cmd.getOptionValue(FILTER_LAYERS_OPTION, null as String?)\n            if (filterRegex != null) {\n                // throw if it's not a valid regex\n                Pattern.compile(filterRegex)\n            }\n\n            val compressOption = cmd.getOptionValue(COMPRESS_OPTION, COMPRESS_OPTION_NONE)\n\n            if (cmd.hasOption(SERVER_ARG)) {\n                return cmd\n            } else if (cmd.options.size == 0 || cmd.hasOption(HELP_OPTION)) {\n                printHelp(options)\n            } else if (Stream\n                    .of<Boolean?>(\n                        cmd.hasOption(INPUT_TILE_ARG),\n                        cmd.hasOption(INPUT_MBTILES_ARG),\n                        cmd.hasOption(INPUT_OFFLINEDB_ARG),\n                        cmd.hasOption(INPUT_PMTILES_ARG),\n                    ).filter { x: Boolean? -> x!! }\n                    .count()\n                != 1L\n            ) {\n                logger.error(\n                    \"Exactly one of --{}, --{}, --{}, or --{} must be used\",\n                    INPUT_TILE_ARG,\n                    INPUT_MBTILES_ARG,\n                    INPUT_OFFLINEDB_ARG,\n                    INPUT_PMTILES_ARG,\n                )\n            } else if (cmd.hasOption(OUTPUT_FILE_ARG) && cmd.hasOption(OUTPUT_DIR_ARG)) {\n                logger.error(\n                    \"Cannot specify both --{} and --{} options.\",\n                    OUTPUT_FILE_ARG,\n                    OUTPUT_DIR_ARG,\n                )\n            } else if (!validateCompression(cmd, compressOption)) {\n                logger.error(\n                    \"Not a valid compression type: '{}'.  Valid options are: {}\",\n                    compressOption,\n                    getAllowedCompressions(cmd).collect(Collectors.joining(\", \")),\n                )\n            } else {\n                return cmd\n            }\n        } catch (ex: IOException) {\n            logger.error(\"Failed to parse command line arguments\", ex)\n        } catch (ex: ParseException) {\n            logger.error(\"Failed to parse command line arguments\", ex)\n        } catch (ex: URISyntaxException) {\n            logger.error(\"Invalid tessellation URL\", ex)\n        }\n        return null\n    }\n\n    private fun printHelp(options: Options?) {\n        val autoUsage = true\n        val header =\n            \"Convert an MVT tile or a container file containing MVT tiles to MLT format.\\n\"\n        val footer =\n            \"\\nExample usages:\\n\" +\n                \" Encode a single tile:\\n\" +\n                \"  encode --mvt input.mvt --mlt output.mlt\\n\" +\n                \" Encode all tiles in an MBTiles file, with compression and parallel encoding:\\n\" +\n                \"  encode --mbtiles input.mbtiles --dir output_dir --compress gzip -j\\n\" +\n                \" Start an encoding server on port 8080:\\n\" +\n                \"  encode --server 8080\\n\\n\" +\n                \"Environment variables:\\n\" +\n                \" \" + ENV_TILE_LOG_INTERVAL + \": Number of tiles between status log messages (default: 10000)\\n\" +\n                \" \" + ENV_COMPRESSION_RATIO_THRESHOLD + \": Minimum compression ratio to apply compression (default: \" +\n                DEFAULT_COMPRESSION_RATIO_THRESHOLD + \")\\n\" +\n                \"  Only applies with --\" + INPUT_MBTILES_ARG + \".\\n\" +\n                \" \" + ENV_COMPRESSION_FIXED_THRESHOLD + \": Minimum savings in bytes for a tile to be compressed (default: \" +\n                \" \" + DEFAULT_COMPRESSION_FIXED_THRESHOLD + \")\\n\" +\n                \"  Only applies with --\" + INPUT_MBTILES_ARG + \".\\n\" +\n                \" \" + ENV_CACHE_MAX + \": Maximum cache size in bytes. (default: \" + DEFAULT_CACHE_MAX + \")\\n\" +\n                \" \" + ENV_CACHE_MAX_HEAP_PERCENT + \": Maximum cache size as a percentage of maximum heap size.\\n\" +\n                \"  Overrides \" + ENV_CACHE_MAX + \" if non-zero. (default: \" + DEFAULT_CACHE_MAX_HEAP_PERCENT + \")\\n\" +\n                \" \" + ENV_CACHE_EXPIRE + \": Cache expiration duration after access, in ISO-8601 format (e.g. P1.2S)\\n\" +\n                \"  or plain (e.g., 1.2s). (default: \" + DEFAULT_CACHE_EXPIRE.toString() + \")\\n\" +\n                \"  Zero causes cache entries to be evicted only due to size.\\n\" +\n                \" \" + ENV_CACHE_BLOCK_SIZE + \": If zero, individual tiles will be cached. If non-zero, the cache will\\n\" +\n                \"  store aligned blocks of that size. (default: \" + DEFAULT_CACHE_BLOCK_SIZE.toString() + \")\\n\" +\n                \" \" + ENV_CACHE_AVERAGE_WEIGHT + \": Average size of tiles in bytes. (default: \" + DEFAULT_CACHE_AVERAGE_WEIGHT + \")\\n\" +\n                \"  Zero disables initial cache size estimation.\\n\" +\n                \" \" + ENV_MAX_TILE_TRACK_SIZE + \": Maximum size of PMTiles tiles to track for duplicate detection. (default: \" +\n                DEFAULT_MAX_TILE_TRACK_SIZE +\n                \")\\n\" +\n                \" All cache options only apply with --\" + INPUT_PMTILES_ARG + \".\\n\"\n\n        val target = TextHelpAppendable(System.err)\n\n        val widthStr = System.getenv(\"COLUMNS\")\n        if (widthStr != null) {\n            try {\n                target.textStyleBuilder.setMaxWidth(widthStr.toInt())\n            } catch (ignore: NumberFormatException) {\n            }\n        }\n\n        val formatter =\n            HelpFormatter\n                .builder()\n                .setShowSince(false)\n                .setHelpAppendable(target)\n                .get()\n        formatter.printHelp(Encode::class.java.name, header, options, null, autoUsage)\n\n        // Passing this as the footer to `printHelp` causes it to be formatted incorrectly.\n        target.append(footer)\n    }\n\n    // matches a layer discriminator, [] = all, [regex] = match a regex\n    private const val LAYER_PATTERN_PREFIX = \"^(?:\\\\[]|\\\\[([^]].+)])?\"\n\n    // A layer discriminator followed by prefix and delimiter.\n    // Delimiter may be a pattern if surrounded by slashes.\n    private val colMapSeparatorPattern1: Pattern =\n        Pattern.compile(LAYER_PATTERN_PREFIX + \"(.+)(/.+/)$\")\n    private val colMapSeparatorPattern2: Pattern = Pattern.compile(LAYER_PATTERN_PREFIX + \"(.+)(.)$\")\n\n    // a layer discriminator followed by a comma-separated list of columns\n    private val colMapListPattern: Pattern = Pattern.compile(LAYER_PATTERN_PREFIX + \"(.+)$\")\n    private val colMapPatternPattern: Pattern = Pattern.compile(\"^/(.*)/$\")\n    private val colMapMatchAll: Pattern = Pattern.compile(\".*\")\n\n    fun getColumnMappings(cmd: CommandLine): ColumnMappingConfig {\n        val result = ColumnMappingConfig()\n        if (cmd.hasOption(COLUMN_MAPPING_AUTO_OPTION)) {\n            throw NotImplementedException(\"Auto column mappings are not implemented yet\")\n        }\n        for (item in cmd.getOptionValues(COLUMN_MAPPING_LIST_OPTION) ?: arrayOf()) {\n            val matcher = colMapListPattern.matcher(item)\n            if (matcher.matches() && matcher.groupCount() == 2) {\n                // matcher doesn't support multiple group matches, split them separately\n                val layers = parseLayerPatterns(matcher.group(1) ?: \"\")\n                val list = matcher.group(2) ?: \"\"\n                val columnNames =\n                    list\n                        .split(\",\")\n                        .map { obj -> obj.trim { it <= ' ' } }\n                        .filter { !it.isEmpty() }\n                        .toList()\n                addColumnMapping(\n                    result,\n                    layers,\n                    ColumnMapping(columnNames, true),\n                )\n            } else {\n                logger.warn(\n                    \"Invalid column mapping ignored: '{}'. Expected pattern is: {}\",\n                    item,\n                    colMapListPattern,\n                )\n            }\n        }\n\n        for (item in cmd.getOptionValues(COLUMN_MAPPING_DELIM_OPTION) ?: arrayOf()) {\n            val matcher =\n                colMapSeparatorPattern1.matcher(item).let {\n                    if (it.matches()) it else colMapSeparatorPattern2.matcher(item)\n                }\n            if (matcher.matches() && matcher.groupCount() == 3) {\n                // matcher doesn't support multiple group matches, split them separately\n                val layers = parseLayerPatterns(matcher.group(1) ?: \"\")\n                val prefix = parsePattern(matcher.group(2) ?: \"\")\n                val delimiter = parsePattern(matcher.group(3) ?: \"\")\n                addColumnMapping(\n                    result,\n                    layers,\n                    ColumnMapping(prefix, delimiter, true),\n                )\n            } else {\n                logger.warn(\n                    \"Invalid column mapping ignored: '{}'. Expected pattern is: {} or {}\",\n                    item,\n                    colMapSeparatorPattern1,\n                    colMapSeparatorPattern2,\n                )\n            }\n        }\n        return result\n    }\n\n    /** Add a new item, appending the mappings to any existing mappings for the same layer pattern */\n    private fun addColumnMapping(\n        result: ColumnMappingConfig,\n        newLayer: Pattern,\n        newMapping: ColumnMapping,\n    ) {\n        result.merge(\n            newLayer,\n            listOf(newMapping),\n        ) { oldList, newList ->\n            Stream.of(oldList, newList).flatMap { it.stream() }.toList()\n        }\n    }\n\n    private fun parseLayerPatterns(pattern: String): Pattern =\n        if (StringUtils.isBlank(pattern)) {\n            colMapMatchAll\n        } else {\n            parsePattern(pattern)\n        }\n\n    private fun parsePattern(pattern: String): Pattern {\n        val matcher = colMapPatternPattern.matcher(pattern)\n        try {\n            if (matcher.matches()) {\n                // A regex surrounded by slashes, return the compiled pattern\n                return Pattern.compile(matcher.group(1))\n            } else {\n                // Just a regular string\n                return Pattern.compile(pattern, Pattern.LITERAL)\n            }\n        } catch (ex: PatternSyntaxException) {\n            logger.error(\"Invalid regex pattern: '{}'.  Matching all input.\", pattern, ex)\n            return colMapMatchAll\n        }\n    }\n\n    private val logger: Logger = LoggerFactory.getLogger(EncodeCommandLine::class.java)\n}\n\nval CommandLine.minZoom get() = getParsedOptionValue<Long>(EncodeCommandLine.MIN_ZOOM_OPTION, 0L).toInt()\nval CommandLine.maxZoom get() = getParsedOptionValue<Long>(EncodeCommandLine.MAX_ZOOM_OPTION, Int.MAX_VALUE.toLong()).toInt()\nval CommandLine.logLevel get() =\n    (\n        if (hasOption(EncodeCommandLine.VERBOSE_OPTION)) {\n            Level.toLevel(getOptionValue(EncodeCommandLine.VERBOSE_OPTION), Level.DEBUG)\n        } else {\n            Level.INFO\n        }\n    ).coerceAtLeast(if (hasOption(EncodeCommandLine.CACHE_STATS_OPTION)) Level.DEBUG else Level.OFF)\nval CommandLine.sortFeaturesPattern get() =\n    if (hasOption(EncodeCommandLine.SORT_FEATURES_OPTION)) {\n        Pattern.compile(getOptionValue(EncodeCommandLine.SORT_FEATURES_OPTION, \".*\"))\n    } else {\n        null\n    }\nval CommandLine.regenIDsPattern get() =\n    if (hasOption(EncodeCommandLine.REGEN_IDS_OPTION)) {\n        Pattern.compile(getOptionValue(EncodeCommandLine.REGEN_IDS_OPTION, \".*\"))\n    } else {\n        null\n    }\nval CommandLine.outlineFeatureTables get() =\n    getOptionValues(EncodeCommandLine.OUTLINE_FEATURE_TABLES_OPTION)\nval CommandLine.useFSSTJava get() = hasOption(EncodeCommandLine.FSST_ENCODING_OPTION)\nval CommandLine.useFSSTNative get() = hasOption(EncodeCommandLine.FSST_NATIVE_ENCODING_OPTION)\nval CommandLine.tessellateSource get() =\n    getOptionValue(EncodeCommandLine.TESSELLATE_URL_OPTION, null as String?)\nval CommandLine.tessellatePolygons get() =\n    (tessellateSource != null) || hasOption(EncodeCommandLine.PRE_TESSELLATE_OPTION)\nval CommandLine.compressionType get() =\n    getOptionValue(EncodeCommandLine.COMPRESS_OPTION, null as String?)\nval CommandLine.enableCoerceOnTypeMismatch get() = hasOption(EncodeCommandLine.ALLOW_COERCE_OPTION)\nval CommandLine.enableElideOnTypeMismatch get() = hasOption(EncodeCommandLine.ALLOW_ELISION_OPTION)\nval CommandLine.filterRegex get() =\n    getOptionValue(EncodeCommandLine.FILTER_LAYERS_OPTION, null as String?)\nval CommandLine.filterPattern get() = filterRegex?.let(Pattern::compile)\nval CommandLine.filterInvert get() = hasOption(EncodeCommandLine.FILTER_LAYERS_INVERT_OPTION)\nval CommandLine.threadCount get() =\n    if (hasOption(EncodeCommandLine.PARALLEL_OPTION)) {\n        getParsedOptionValue<Long>(EncodeCommandLine.PARALLEL_OPTION, 0L).toInt()\n    } else {\n        1\n    }.let {\n        if (it > 0) {\n            it\n        } else {\n            Runtime\n                .getRuntime()\n                .availableProcessors()\n        }\n    }\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/EncodeConfig.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.maplibre.mlt.compare.CompareHelper.CompareMode\nimport org.maplibre.mlt.converter.ColumnMappingConfig\nimport org.maplibre.mlt.converter.ConversionConfig\nimport java.net.URI\nimport java.util.regex.Pattern\n\ndata class EncodeConfig(\n    val columnMappingConfig: ColumnMappingConfig,\n    val conversionConfig: ConversionConfig,\n    val tessellateSource: URI?,\n    val sortFeaturesPattern: Pattern?,\n    val regenIDsPattern: Pattern?,\n    val compressionType: String?,\n    val minZoom: Int,\n    val maxZoom: Int,\n    val willOutput: Boolean,\n    val willDecode: Boolean,\n    val willPrintMLT: Boolean,\n    val willPrintMVT: Boolean,\n    val compareProp: Boolean,\n    val compareGeom: Boolean,\n    val willTime: Boolean,\n    val taskRunner: TaskRunner,\n    val continueOnError: Boolean,\n    val logCacheStats: Boolean,\n) {\n    val compareMode get() =\n        if (compareGeom && compareProp) {\n            CompareMode.All\n        } else if (compareGeom) {\n            CompareMode.Geometry\n        } else if (compareProp) {\n            CompareMode.Properties\n        } else {\n            CompareMode.None\n        }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Environment.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport kotlin.time.Duration\nimport kotlin.time.Duration.Companion.seconds\n\nconst val ENV_COMPRESSION_RATIO_THRESHOLD = \"MLT_COMPRESSION_RATIO_THRESHOLD\"\nconst val DEFAULT_COMPRESSION_RATIO_THRESHOLD = 0.98\nval compressionRatioThreshold by lazy { computeCompressionRatioThreshold() }\n\nconst val ENV_COMPRESSION_FIXED_THRESHOLD = \"MLT_COMPRESSION_FIXED_THRESHOLD\"\nconst val DEFAULT_COMPRESSION_FIXED_THRESHOLD = 20L\nval compressionFixedThreshold by lazy { computeCompressionFixedThreshold() }\n\nconst val ENV_TILE_LOG_INTERVAL = \"MLT_TILE_LOG_INTERVAL\"\nconst val DEFAULT_TILE_LOG_INTERVAL = 10_000L\nval tileLogInterval by lazy { computeTileLogInterval() }\n\nconst val ENV_CACHE_MAX = \"MLT_CACHE_MAX\"\nval DEFAULT_CACHE_MAX = 64L * 1024L * 1024L\nval cacheMaxSize by lazy { computeCacheMaxSize() }\n\n// Default percentage is zero; a non-zero percentage will override the fixed size.\nconst val DEFAULT_CACHE_MAX_HEAP_PERCENT = 0.0\nconst val ENV_CACHE_MAX_HEAP_PERCENT = \"MLT_CACHE_MAX_HEAP_PERCENT\"\nval cacheMaxHeapPercent by lazy { computeCacheMaxHeapPercent() }\n\nconst val ENV_CACHE_EXPIRE = \"MLT_CACHE_EXPIRE\"\nval DEFAULT_CACHE_EXPIRE = 0.seconds\nval cacheExpireAfterAccess by lazy { computeCacheExpireAfterAccess() }\n\nconst val ENV_THREAD_QUEUE_SIZE = \"MLT_THREAD_QUEUE_SIZE\"\nconst val DEFAULT_THREAD_QUEUE_SIZE = 10_000\nval threadQueueSize by lazy { computeThreadQueueSize() }\n\nconst val ENV_CACHE_AVERAGE_WEIGHT = \"MLT_CACHE_AVERAGE_SIZE\"\nconst val DEFAULT_CACHE_AVERAGE_WEIGHT = 4 * 1024\nval cacheAverageEntrySize by lazy { computeCacheAverageEntrySize() }\n\nconst val ENV_CACHE_BLOCK_SIZE = \"MLT_CACHE_BLOCK_SIZE\"\nconst val DEFAULT_CACHE_BLOCK_SIZE = 16 * 1024\nval cacheBlockSize by lazy { computeCacheBlockSize() }\n\nconst val ENV_MAX_TILE_TRACK_SIZE = \"MLT_MAX_TILE_TRACK_SIZE\"\nconst val DEFAULT_MAX_TILE_TRACK_SIZE = 1024\nval maxTileTrackSize by lazy { computeMaxTileTrackSize() }\n\nval defaultEnvResolver: (String) -> String? = { name -> System.getenv(name) }\n\n/** Use the provided function to resolve environment variables.\n */\nvar envResolver = defaultEnvResolver\n\nprivate fun <T> resolveConfigValue(\n    name: String,\n    def: T,\n    parser: (String) -> T,\n): T =\n    try {\n        envResolver(name)?.let { parser(it) } ?: def\n    } catch (e: Exception) {\n        logger.warn(\"Failed to parse {}, using default value {}\", name, def, e)\n        def\n    }\n\ninternal fun computeCompressionRatioThreshold() =\n    resolveConfigValue(ENV_COMPRESSION_RATIO_THRESHOLD, DEFAULT_COMPRESSION_RATIO_THRESHOLD, String::toDouble).also {\n        if (it < 0.0) {\n            throw IllegalArgumentException(\"Compression ratio threshold must be non-negative\")\n        }\n    }\n\ninternal fun computeCompressionFixedThreshold() =\n    resolveConfigValue(ENV_COMPRESSION_FIXED_THRESHOLD, DEFAULT_COMPRESSION_FIXED_THRESHOLD, String::toLong)\n\ninternal fun computeTileLogInterval() =\n    resolveConfigValue(ENV_TILE_LOG_INTERVAL, DEFAULT_TILE_LOG_INTERVAL, String::toLong).let {\n        // treat zero or less as \"never\"\n        if (it < 1L) Long.MAX_VALUE else it\n    }\n\ninternal fun computeCacheMaxSize() =\n    resolveConfigValue(ENV_CACHE_MAX, DEFAULT_CACHE_MAX, String::toLong).let {\n        if (it > 0L) it else throw IllegalArgumentException(\"Cache maximum size must be positive\")\n    }\n\ninternal fun computeCacheMaxHeapPercent(): Double =\n    resolveConfigValue(ENV_CACHE_MAX_HEAP_PERCENT, DEFAULT_CACHE_MAX_HEAP_PERCENT, String::toDouble).also {\n        if (!it.isFinite() || !(0.0 <= it && it < 100.0)) {\n            throw IllegalArgumentException(\"Cache max heap percent must be between 0 and 100\")\n        }\n    }\n\ninternal fun computeCacheExpireAfterAccess() =\n    resolveConfigValue(ENV_CACHE_EXPIRE, DEFAULT_CACHE_EXPIRE, Duration::parse).also {\n        if (it.isNegative()) {\n            throw IllegalArgumentException(\"Cache expire duration must be non-negative\")\n        }\n    }\n\ninternal fun computeThreadQueueSize() =\n    resolveConfigValue(ENV_THREAD_QUEUE_SIZE, DEFAULT_THREAD_QUEUE_SIZE, String::toInt).also {\n        if (it < 1) {\n            throw IllegalArgumentException(\"Thread queue size must be positive\")\n        }\n    }\n\ninternal fun computeCacheAverageEntrySize() =\n    resolveConfigValue(ENV_CACHE_AVERAGE_WEIGHT, DEFAULT_CACHE_AVERAGE_WEIGHT, String::toInt).also {\n        if (it < 0) {\n            throw IllegalArgumentException(\"Cache average weight must not be negative\")\n        }\n    }\n\ninternal fun computeCacheBlockSize() =\n    resolveConfigValue(ENV_CACHE_BLOCK_SIZE, DEFAULT_CACHE_BLOCK_SIZE, String::toInt).also {\n        if (it < 0) {\n            throw IllegalArgumentException(\"Cache block size must not be negative\")\n        }\n    }\n\ninternal fun computeMaxTileTrackSize() =\n    resolveConfigValue(ENV_MAX_TILE_TRACK_SIZE, DEFAULT_MAX_TILE_TRACK_SIZE, String::toInt).let { it.coerceAtLeast(0) }\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Json.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.maplibre.mlt.data.MapLibreTile\nimport org.maplibre.mlt.data.MapboxVectorTile\nimport org.maplibre.mlt.json.Json\n\nfun MapboxVectorTile.toJson(pretty: Boolean = true): String = Json.toJson(this, pretty)\n\nfun MapLibreTile.toJson(pretty: Boolean = true): String = Json.toJson(this, pretty)\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Logging.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.slf4j.LoggerFactory\nimport org.slf4j.MarkerFactory\nimport java.text.DecimalFormat\nimport kotlin.math.log10\nimport kotlin.math.pow\n\n/** Logger class for free functions */\nprivate class CommonLogger {\n    private constructor()\n}\n\ninternal val logger = LoggerFactory.getLogger(CommonLogger::class.java)\ninternal val readMarker = MarkerFactory.getMarker(\"READ\")\ninternal val writeMarker = MarkerFactory.getMarker(\"WRITE\")\ninternal val cacheMarker = MarkerFactory.getMarker(\"CACHE\")\ninternal val compressMarker = MarkerFactory.getMarker(\"COMPRESSION\")\ninternal val colMapMarker = MarkerFactory.getMarker(\"COLMAP\")\n\nprivate val sizeUnits = arrayOf<String>(\"B\", \"kiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\")\n\n// org.apache.commons.io.FileUtils.byteCountToDisplaySize does this, but always rounds down to GB\nfun formatSize(size: Long): String {\n    if (size <= 0) return \"0\"\n    val digitGroups = Math.floor((log10(size.toDouble()) / log10(1024.0)))\n    val formatter = DecimalFormat(\"#,##0.#\")\n    return formatter.format(size / 1024.0.pow(digitGroups)) + \" \" + sizeUnits[digitGroups.toInt()]\n}\n\nfun formatNanosecDuration(nanos: Double) =\n    when (nanos) {\n        in 1e9..Double.MAX_VALUE -> String.format(\"%.2fs\", nanos / 1e9)\n        in 1e6..1e9 -> String.format(\"%.2fms\", nanos / 1e6)\n        in 1e3..1e6 -> String.format(\"%.2fus\", nanos / 1e3)\n        in Double.MIN_VALUE..1e3 -> String.format(\"%.2fns\", nanos)\n        else -> \"0\"\n    }\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/MBTiles.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.commons.lang3.mutable.MutableBoolean\nimport org.imintel.mbtiles4j.MBTilesReadException\nimport org.imintel.mbtiles4j.MBTilesReader\nimport org.imintel.mbtiles4j.MBTilesWriteException\nimport org.imintel.mbtiles4j.MBTilesWriter\nimport org.imintel.mbtiles4j.Tile\nimport org.imintel.mbtiles4j.model.MetadataEntry\nimport org.maplibre.mlt.converter.MltConverter\nimport org.maplibre.mlt.metadata.tileset.MltMetadata\nimport java.io.ByteArrayInputStream\nimport java.io.File\nimport java.io.IOException\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.sql.Connection\nimport java.sql.DriverManager\nimport java.sql.SQLException\nimport java.util.Optional\nimport java.util.concurrent.atomic.AtomicBoolean\nimport java.util.concurrent.atomic.AtomicLong\n\n/** Encode the entire contents of an MBTile file of MVT tiles */\nfun encodeMBTiles(\n    inputMBTilesPath: String,\n    outputPath: Path?,\n    config: EncodeConfig,\n): Boolean {\n    var mbTilesReader: MBTilesReader? = null\n    val success = AtomicBoolean(true)\n    try {\n        mbTilesReader = MBTilesReader(File(inputMBTilesPath))\n\n        // Remove any existing output file, as SQLite will add to it instead of replacing\n        if (outputPath != null) {\n            Files.deleteIfExists(outputPath)\n        }\n\n        // If no target file is specified, a temporary is used.\n        // mbtiles4j blocks access to in-memory databases and built-in\n        // temp file support, so we have to use its temp file support.\n        // Nullable so that we can close it correctly in normal and exception scenarios.\n        var mbTilesWriter: MBTilesWriter? =\n            if (outputPath != null) {\n                MBTilesWriter(outputPath.toFile())\n            } else {\n                MBTilesWriter(\"encode\")\n            }\n        try {\n            // Copy metadata from the input file.\n            // We can't set the format, see the coda.\n            val metadata = mbTilesReader.getMetadata()\n\n            // As of 1.0.6, mbtiles4j doesn't correctly handle metadata entries with SQL special\n            // characters, so we have to handle these later, once we have direct access to the file.\n            // mbTilesWriter.addMetadataEntry(metadata)\n\n            val pbMeta = MltMetadata.TileSetMetadata()\n            pbMeta.name = metadata.tilesetName\n            pbMeta.attribution = metadata.attribution\n            pbMeta.description = metadata.tilesetDescription\n\n            val bounds = metadata.tilesetBounds\n            pbMeta.bounds =\n                listOf(\n                    bounds.left,\n                    bounds.top,\n                    bounds.right,\n                    bounds.bottom,\n                )\n\n            val metadataJSON = MltConverter.createTilesetMetadataJSON(pbMeta)\n\n            // Read everything on the main thread for simplicity.\n            // Splitting reads by zoom level might improve performance.\n            val tiles = mbTilesReader.getTiles()\n            val tileCount = AtomicLong(0)\n            val writerCapture = mbTilesWriter!!\n            try {\n                while (tiles.hasNext()) {\n                    val tile = tiles.next()\n                    try {\n                        if (!config.continueOnError && !success.get()) {\n                            break\n                        }\n\n                        val x = tile.column\n                        val y = tile.row\n                        val z = tile.zoom\n\n                        if (z < config.minZoom || z > config.maxZoom) {\n                            logger.trace(\"Skipping tile {}:{},{} outside zoom range\", z, x, y)\n                            continue\n                        }\n\n                        val data = tile.data.readAllBytes()\n\n                        config.taskRunner.run(\n                            Runnable {\n                                val count = tileCount.incrementAndGet()\n                                if ((count % tileLogInterval) == 0L) {\n                                    logger.trace(\n                                        \"Processing tile {} : {}:{},{}\",\n                                        count,\n                                        z,\n                                        x,\n                                        y,\n                                    )\n                                }\n                                if (!convertTile(\n                                        config,\n                                        data,\n                                        writerCapture,\n                                        tile,\n                                    )\n                                ) {\n                                    success.set(false)\n                                }\n                            },\n                        )\n                    } catch (ex: IllegalArgumentException) {\n                        success.set(false)\n                        logger.error(\n                            \"Failed to convert tile {}:{},{}\",\n                            tile.zoom,\n                            tile.column,\n                            tile.row,\n                            ex,\n                        )\n                    }\n                }\n\n                try {\n                    config.taskRunner.shutdown()\n                    config.taskRunner.awaitTermination()\n                } catch (ex: InterruptedException) {\n                    logger.error(\"Interrupted\", ex)\n                    return false\n                }\n\n                if (!config.continueOnError && !success.get()) {\n                    return false\n                }\n\n                val dbFile = mbTilesWriter.close()\n                if (outputPath == null) {\n                    dbFile.deleteOnExit()\n                }\n                mbTilesWriter = null\n\n                // mbtiles4j doesn't support types other than png and jpg,\n                // so we have to set the format metadata the hard way.\n                getConnection(dbFile).use { connection ->\n                    updateMetadata(config, connection, metadata, metadataJSON)\n                }\n            } finally {\n                // mbtiles4j doesn't support `AutoCloseable`\n                tiles.close()\n            }\n        } finally {\n            if (mbTilesWriter != null) {\n                val file = mbTilesWriter.close()\n                if (outputPath == null) {\n                    file.delete()\n                }\n            }\n        }\n    } catch (ex: MBTilesReadException) {\n        success.set(false)\n        logger.error(\"Failed to convert MBTiles file\", ex)\n    } catch (ex: IOException) {\n        success.set(false)\n        logger.error(\"Failed to convert MBTiles file\", ex)\n    } catch (ex: MBTilesWriteException) {\n        success.set(false)\n        logger.error(\"Failed to convert MBTiles file\", ex)\n    } catch (ex: SQLException) {\n        success.set(false)\n        logger.error(\"Failed to convert MBTiles file\", ex)\n    } finally {\n        if (mbTilesReader != null) {\n            mbTilesReader.close()\n        }\n    }\n    return success.get()\n}\n\nprivate fun getConnectionString(dbFile: File) = \"jdbc:sqlite:\" + dbFile.absolutePath\n\nprivate fun getConnection(dbFile: File) = DriverManager.getConnection(getConnectionString(dbFile))\n\nprivate fun updateMetadata(\n    config: EncodeConfig,\n    connection: Connection,\n    metadata: MetadataEntry,\n    metadataJSON: String,\n) {\n    logger.debug(\"Updating metadata\")\n\n    var sql = \"INSERT OR REPLACE INTO metadata (name, value) VALUES (?, ?)\"\n    connection.prepareStatement(sql).use { statement ->\n        for (entry in metadata.requiredKeyValuePairs) {\n            statement.setString(1, entry.key)\n            statement.setString(2, entry.value)\n            statement.addBatch()\n        }\n        for (entry in metadata.customKeyValuePairs) {\n            statement.setString(1, entry.key)\n            statement.setString(2, entry.value)\n            statement.addBatch()\n        }\n\n        logger.trace(\"Setting tile MIME type to '{}'\", MBTILES_METADATA_MIME_TYPE)\n        statement.setString(1, \"format\")\n        statement.setString(2, MBTILES_METADATA_MIME_TYPE)\n        statement.addBatch()\n\n        // Put the global metadata in a custom metadata key.\n        // Could also be in a custom key within the standard `json` entry...\n        logger.trace(\"Adding tileset metadata JSON: {}\", metadataJSON)\n        statement.setString(1, \"mln-json\")\n        statement.setString(2, metadataJSON)\n        statement.addBatch()\n\n        statement.executeBatch()\n    }\n\n    vacuumDatabase(connection)\n}\n\nprivate fun convertTile(\n    config: EncodeConfig,\n    data: ByteArray,\n    writerCapture: MBTilesWriter,\n    tile: Tile,\n): Boolean {\n    try {\n        val x = tile.column\n        val y = tile.row\n        val z = tile.zoom\n        val srcTileData = decompress(ByteArrayInputStream(data))\n        val didCompress = MutableBoolean(false)\n        val tileData =\n            convertTile(\n                x.toLong(),\n                y.toLong(),\n                z,\n                srcTileData,\n                config,\n                Optional.of(compressionRatioThreshold),\n                Optional.of(compressionFixedThreshold),\n                didCompress,\n            )\n\n        if (tileData != null) {\n            synchronized(writerCapture) {\n                writerCapture.addTile(tileData, z.toLong(), x.toLong(), y.toLong())\n            }\n        }\n        return true\n    } catch (ex: IOException) {\n        logger.error(\n            \"Failed to convert tile {}:{},{}\",\n            tile.zoom,\n            tile.column,\n            tile.row,\n            ex,\n        )\n    } catch (ex: MBTilesWriteException) {\n        logger.error(\n            \"Failed to convert tile {}:{},{}\",\n            tile.zoom,\n            tile.column,\n            tile.row,\n            ex,\n        )\n    }\n    return false\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/OfflineDB.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport com.google.gson.Gson\nimport com.google.gson.JsonObject\nimport com.google.gson.JsonSyntaxException\nimport org.apache.commons.lang3.mutable.MutableBoolean\nimport java.io.ByteArrayInputStream\nimport java.io.ByteArrayOutputStream\nimport java.io.File\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.nio.file.StandardCopyOption\nimport java.sql.Connection\nimport java.sql.DriverManager\nimport java.sql.PreparedStatement\nimport java.sql.SQLException\nimport java.util.Optional\nimport java.util.concurrent.atomic.AtomicBoolean\nimport java.util.concurrent.atomic.AtomicLong\n\n/** Encode the MVT tiles in an offline database file */\nfun encodeOfflineDB(\n    inputPath: Path,\n    outputPath: Path?,\n    config: EncodeConfig,\n): Boolean {\n    // Start with a copy of the source file so we don't have to rebuild the complex schema\n    var outputPath = outputPath\n    try {\n        if (outputPath == null) {\n            val tempFile = File.createTempFile(\"encode-\", \"-db\")\n            outputPath = tempFile.toPath()\n            tempFile.deleteOnExit()\n        }\n        logger.debug(\"Copying source file {} to {}\", inputPath, outputPath)\n        Files.copy(\n            inputPath,\n            outputPath,\n            StandardCopyOption.REPLACE_EXISTING,\n            StandardCopyOption.COPY_ATTRIBUTES,\n        )\n    } catch (ex: IOException) {\n        logger.error(\"Failed to create target file\", ex)\n        return false\n    }\n\n    Class.forName(\"org.sqlite.JDBC\")\n    val srcConnectionString = \"jdbc:sqlite:\" + inputPath.toAbsolutePath()\n    val dstConnectionString = \"jdbc:sqlite:\" + outputPath.toAbsolutePath()\n    val updateSql = \"UPDATE tiles SET data = ?, compressed = ? WHERE id = ?\"\n    val success = AtomicBoolean(true)\n    try {\n        DriverManager.getConnection(srcConnectionString).use { srcConnection ->\n            DriverManager.getConnection(dstConnectionString).use { dstConnection ->\n                srcConnection.createStatement().use { iterateStatement ->\n                    iterateStatement.executeQuery(\"SELECT * FROM tiles\").use { tileResults ->\n                        dstConnection.prepareStatement(updateSql).use { updateStatement ->\n                            val tileCount = AtomicLong(0)\n                            while (tileResults.next()) {\n                                if (!config.continueOnError && !success.get()) {\n                                    break\n                                }\n\n                                // Read on the main thread. Could be split by zoom level, etc., if needed.\n                                val uniqueID = tileResults.getLong(\"id\")\n                                val x = tileResults.getInt(\"x\")\n                                val y = tileResults.getInt(\"y\")\n                                val z = tileResults.getInt(\"z\")\n                                val data = tileResults.getBinaryStream(\"data\").readAllBytes()\n\n                                config.taskRunner.run(\n                                    {\n                                        val count = tileCount.incrementAndGet()\n                                        if ((count % tileLogInterval) == 0L) {\n                                            logger.debug(\n                                                \"Processing tile {} : {}:{},{}\",\n                                                count,\n                                                z,\n                                                x,\n                                                y,\n                                            )\n                                        }\n                                        if (!convertTile(\n                                                config,\n                                                z,\n                                                x,\n                                                y,\n                                                data,\n                                                uniqueID,\n                                                updateStatement,\n                                            )\n                                        ) {\n                                            success.set(false)\n                                        }\n                                    },\n                                )\n                            }\n                            try {\n                                config.taskRunner.shutdown()\n                                config.taskRunner.awaitTermination()\n                            } catch (ex: InterruptedException) {\n                                logger.error(\"Interrupted\", ex)\n                                return false\n                            }\n                        }\n                    }\n                }\n                if (!config.continueOnError && !success.get()) {\n                    return false\n                }\n\n                updateMetadata(config, dstConnection)\n                vacuumDatabase(dstConnection)\n            }\n        }\n    } catch (ex: SQLException) {\n        logger.error(\"Offline Database conversion failed\", ex)\n        return false\n    } catch (ex: IOException) {\n        logger.error(\"Offline Database conversion failed\", ex)\n        return false\n    }\n    return success.get()\n}\n\nprivate fun updateMetadata(\n    config: EncodeConfig,\n    dstConnection: Connection,\n) {\n    val metadataKind = 2 // `mbgl::Resource::Kind::Source`\n    val metadataQuerySQL = \"SELECT id,data FROM resources WHERE kind = ?\"\n    val metadataUpdateSQL = \"UPDATE resources SET data = ?, compressed = ? WHERE id = ?\"\n    dstConnection.prepareStatement(metadataQuerySQL).use { queryStatement ->\n        dstConnection.prepareStatement(metadataUpdateSQL).use { updateStatement ->\n            queryStatement.setInt(1, metadataKind)\n            val metadataResults = queryStatement.executeQuery()\n            while (metadataResults.next()) {\n                val uniqueID = metadataResults.getLong(\"id\")\n                var data: ByteArray?\n                try {\n                    data = decompress(metadataResults.getBinaryStream(\"data\"))\n                } catch (ignore: IOException) {\n                    logger.warn(\"Failed to decompress Source resource '{}', skipping\", uniqueID)\n                    continue\n                } catch (ignore: IllegalStateException) {\n                    logger.warn(\"Failed to decompress Source resource '{}', skipping\", uniqueID)\n                    continue\n                }\n\n                // Parse JSON\n                var jsonString = String(data, StandardCharsets.UTF_8)\n                val json: JsonObject\n                try {\n                    json = Gson().fromJson<JsonObject>(jsonString, JsonObject::class.java)\n                } catch (ex: JsonSyntaxException) {\n                    logger.warn(\"Source resource '{}' is not JSON, skipping\", uniqueID)\n                    continue\n                }\n\n                // Update the format field\n                json.addProperty(\"format\", MBTILES_METADATA_MIME_TYPE)\n\n                // Re-serialize\n                jsonString = json.toString()\n\n                // Re-compress\n                val compressed: Boolean\n                ByteArrayOutputStream().use { outputStream ->\n                    createCompressStream(\n                        outputStream,\n                        config.compressionType,\n                    ).use { compressStream ->\n                        compressed = (compressStream !== outputStream)\n                        compressStream.write(jsonString.toByteArray(StandardCharsets.UTF_8))\n                    }\n                    data = outputStream.toByteArray()\n                }\n                // Update the database\n                updateStatement.setBytes(1, data)\n                updateStatement.setBoolean(2, compressed)\n                updateStatement.setLong(3, uniqueID)\n                updateStatement.execute()\n\n                logger.debug(\n                    \"Updated source JSON format to '{}'\",\n                    MBTILES_METADATA_MIME_TYPE,\n                )\n            }\n        }\n    }\n}\n\nprivate fun convertTile(\n    config: EncodeConfig,\n    z: Int,\n    x: Int,\n    y: Int,\n    data: ByteArray,\n    uniqueID: Long,\n    updateStatement: PreparedStatement,\n): Boolean {\n    logger.trace(\"Converting tile {}: {},{}\", z, x, y)\n\n    val srcTileData: ByteArray?\n    try {\n        srcTileData = decompress(ByteArrayInputStream(data))\n    } catch (ex: IOException) {\n        logger.error(\"Failed to decompress tile '{}'\", uniqueID, ex)\n        return false\n    } catch (ex: IllegalStateException) {\n        logger.error(\"Failed to decompress tile '{}'\", uniqueID, ex)\n        return false\n    }\n\n    val didCompress = MutableBoolean(false)\n    val tileData =\n        convertTile(\n            x.toLong(),\n            y.toLong(),\n            z,\n            srcTileData,\n            config,\n            Optional.of(compressionRatioThreshold),\n            Optional.of(compressionFixedThreshold),\n            didCompress,\n        )\n\n    if (tileData != null) {\n        try {\n            // Parallel writes are possible, but only by creating a separate connection for each thread\n            synchronized(updateStatement) {\n                updateStatement.setBytes(1, tileData)\n                updateStatement.setBoolean(2, didCompress.booleanValue())\n                updateStatement.setLong(3, uniqueID)\n                updateStatement.execute()\n            }\n            return true\n        } catch (ex: SQLException) {\n            logger.error(\"Failed to convert tile '{}'\", uniqueID, ex)\n        }\n    }\n    return false\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/PMTiles.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport com.github.benmanes.caffeine.cache.RemovalCause\nimport com.github.benmanes.caffeine.cache.Scheduler\nimport com.onthegomap.planetiler.archive.TileArchiveMetadata\nimport com.onthegomap.planetiler.archive.TileArchiveWriter\nimport com.onthegomap.planetiler.archive.TileCompression\nimport com.onthegomap.planetiler.archive.TileEncodingResult\nimport com.onthegomap.planetiler.archive.TileFormat\nimport com.onthegomap.planetiler.archive.WriteableTileArchive\nimport com.onthegomap.planetiler.geo.TileCoord\nimport com.onthegomap.planetiler.pmtiles.Pmtiles\nimport com.onthegomap.planetiler.pmtiles.Pmtiles.TileType\nimport com.onthegomap.planetiler.pmtiles.WriteablePmtiles\nimport io.tileverse.cache.CacheManager\nimport io.tileverse.cache.CaffeineCache\nimport io.tileverse.cache.CaffeineCacheManager\nimport io.tileverse.io.ByteRange\nimport io.tileverse.rangereader.AbstractRangeReader\nimport io.tileverse.rangereader.RangeReader\nimport io.tileverse.rangereader.RangeReaderFactory\nimport io.tileverse.rangereader.cache.CachingRangeReader\nimport org.apache.commons.lang3.ArrayUtils\nimport org.apache.commons.lang3.mutable.MutableBoolean\nimport java.io.ByteArrayInputStream\nimport java.io.IOException\nimport java.lang.ref.WeakReference\nimport java.net.URI\nimport java.nio.ByteBuffer\nimport java.nio.file.Path\nimport java.util.Optional\nimport java.util.OptionalLong\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.RejectedExecutionException\nimport java.util.concurrent.atomic.AtomicBoolean\nimport java.util.concurrent.atomic.AtomicLong\nimport kotlin.math.max\nimport kotlin.math.min\nimport kotlin.time.toJavaDuration\n\n/** Encode the MVT tiles in a PMTiles file */\nfun encodePMTiles(\n    inputURI: URI,\n    outputPath: Path,\n    config: EncodeConfig,\n): Boolean {\n    val cacheManager = getCacheManager(config)\n    return RangeReaderFactory.create(inputURI).use { rawReader ->\n        logger.debug(\"Opened '{}' for reading\", inputURI)\n        getCachingReader(rawReader, cacheManager).use { cachingReader ->\n            val adapter = getReaderAdapter(cachingReader)\n            ReadablePmtiles(adapter)\n                .use { pmTilesReader ->\n                    WriteablePmtiles.newWriteToFile(outputPath).use { writer ->\n                        logger.debug(\"Opened '{}' for writing\", outputPath)\n                        encodePMTiles(inputURI, pmTilesReader, writer, outputPath, config)\n                    }\n                }.also {\n                    if (config.logCacheStats) {\n                        rangeReaderCache.get()?.also(::logCacheStats)\n                    }\n                }\n        }\n    }\n}\n\n// To have any effect on the cache size limit, we have to entirely replace the cache setup.\n// See `io.tileverse.rangereader.cache.RangeReaderCache.buildSharedCache`\nprivate fun getCacheManager(config: EncodeConfig) =\n    CaffeineCacheManager().also {\n        // `RangeReaderCache.SHARED_CACHE_NAME` is private\n        val cacheName = \"tileverse-rangereader-cache\"\n        val maxMemory = Runtime.getRuntime().maxMemory()\n        val cacheSize =\n            if (cacheMaxHeapPercent > 0) {\n                (cacheMaxHeapPercent / 100 * maxMemory).toLong()\n            } else {\n                cacheMaxSize\n            }\n\n        logger.debug(\n            \"Initializing cache with max size {}, max age {}\",\n            formatSize(cacheSize),\n            cacheExpireAfterAccess,\n        )\n        rangeReaderCache =\n            WeakReference(\n                it.getCache(cacheName, {\n                    CaffeineCache\n                        .newBuilder<ByteRange, ByteBuffer>()\n                        .maximumWeight(cacheSize)\n                        .weigher(::weigh)\n                        .scheduler(Scheduler.systemScheduler())\n                        .also {\n                            if (cacheAverageEntrySize > 0) {\n                                it.averageWeight(weigh(cacheAverageEntrySize))\n                            }\n                            if (cacheExpireAfterAccess.isPositive()) {\n                                it.expireAfterAccess(cacheExpireAfterAccess.toJavaDuration())\n                            }\n                            if (config.logCacheStats) {\n                                it.recordStats()\n                                it.removalListener(::onRemoval)\n                            }\n                        }.build()\n                }),\n            )\n    }\n\nprivate var rangeReaderCache: WeakReference<CaffeineCache<ByteRange, ByteBuffer>> = WeakReference(null)\n\nprivate fun getCachingReader(\n    rawReader: RangeReader,\n    cacheManager: CacheManager,\n) = CachingRangeReader\n    .builder(rawReader)\n    .cacheManager(cacheManager)\n    .withoutHeaderBuffer()\n    .blockSize(cacheBlockSize)\n    .build()\n\nprivate fun logCacheStats(cache: CaffeineCache<ByteRange, ByteBuffer>) {\n    val stats = cache.stats()\n    val runtime = Runtime.getRuntime()\n    val usedMem = runtime.totalMemory() - runtime.freeMemory()\n    val freeMem = runtime.maxMemory() - usedMem\n    logger.debug(\n        cacheMarker,\n        \"Cache: entries={} hits={} ({}%) misses={} averageLoadTime={} evictions={} evictionWeight={} free mem={}\",\n        String.format(\"%,d\", stats.entryCount),\n        String.format(\"%,d\", stats.hitCount),\n        String.format(\"%.1f\", 100 * stats.hitRate),\n        String.format(\"%,d\", stats.missCount),\n        formatNanosecDuration(stats.averageLoadTime),\n        String.format(\"%,d\", stats.evictionCount),\n        formatSize(stats.evictionWeight),\n        formatSize(freeMem),\n    )\n}\n\nprivate fun getReaderAdapter(reader: AbstractRangeReader) =\n    object : ReadablePmtiles.DataReader {\n        override fun read(\n            offset: Long,\n            length: Int,\n        ) = reader.readRange(offset, length).array()\n    }\n\nprivate fun weigh(\n    key: ByteRange,\n    value: ByteBuffer,\n) = weigh(value.capacity())\n\nprivate fun weigh(size: Int): Int {\n    // Approximate overhead of the key record (long + int + object header)\n    val keyWeight = 24\n    // Value weight: object header + int field + the actual data in the ByteBuffer\n    val valueWeight = 16 + size\n    return keyWeight + valueWeight\n}\n\nprivate fun onRemoval(\n    key: ByteRange?,\n    value: ByteBuffer?,\n    cause: RemovalCause,\n) {\n    if (logger.isTraceEnabled && cause != RemovalCause.EXPLICIT) {\n        logger.trace(cacheMarker, \"Evicted {}, {}, cause: {}\", value, key, cause)\n    }\n}\n\nprivate fun encodePMTiles(\n    inputURI: URI,\n    reader: ReadablePmtiles,\n    writer: WriteablePmtiles,\n    outputPath: Path,\n    config: EncodeConfig,\n): Boolean {\n    val timer = if (config.willTime) Timer() else null\n    val header = reader.header\n\n    if (logger.isDebugEnabled) {\n        val tilesPresentPct =\n            if (header.numAddressedTiles > 0) {\n                String.format(\n                    \" (%.1f%%)\",\n                    100.0 * header.numTileContents / header.numAddressedTiles,\n                )\n            } else {\n                \"\"\n            }\n\n        logger.debug(\n            \"PMTiles: version={}, tileType={}, tileCompression={}, minZoom={}, maxZoom={}, numAddressedTiles={}, numTileContents={}{}\",\n            header.specVersion,\n            header.tileType,\n            header.tileCompression,\n            header.minZoom,\n            header.maxZoom,\n            String.format(\"%,d\", header.numAddressedTiles),\n            String.format(\"%,d\", header.numTileContents),\n            tilesPresentPct,\n        )\n    }\n\n    if (header.tileType != TileType.MVT) {\n        logger.error(\n            \"Input PMTiles tile type is {}, expected {}\",\n            header.tileType,\n            TileType.MVT,\n        )\n        return false\n    }\n\n    // If a compression type is given (including none) try to use that, otherwise\n    // use the source compression type mapped to supported types.\n    val targetCompressType =\n        if (config.compressionType == null) {\n            toTileCompression(header.tileCompression)\n        } else {\n            TileCompression.fromId(config.compressionType)\n        }\n\n    val minZoom = max(header.minZoom.toInt(), config.minZoom)\n    val maxZoom = min(header.maxZoom.toInt(), config.maxZoom)\n\n    // re-create the config with the resolved compression type\n    val targetConfig = config.copy(compressionType = toCompressionOption(targetCompressType))\n\n    val newMetadata =\n        updateMetadata(reader.metadata(), minZoom, maxZoom, targetCompressType)\n    val state = ConversionState(targetConfig)\n\n    try {\n        writer.newTileWriter().use { tileWriter ->\n            // The root task which creates other tasks for converting each tile coordinate range.\n            // Completes when the directory has been fully read and all tasks have been created.\n            config.taskRunner.run(\n                {\n                    processTiles(\n                        reader.getTileCoordRanges(minZoom, maxZoom),\n                        reader,\n                        tileWriter,\n                        config.taskRunner,\n                        config,\n                        state,\n                    )\n                    config.taskRunner.shutdown()\n                },\n            )\n\n            try {\n                // Wait for all tasks to finish\n                config.taskRunner.awaitTermination()\n            } catch (ex: InterruptedException) {\n                logger.error(\"Interrupted\", ex)\n                state.success.set(false)\n            }\n\n            if (state.success.get()) {\n                logger.debug(\"Finalizing PMTiles file\")\n                writer.finish(newMetadata)\n            }\n\n            timer?.stop(\"PMTiles\")\n\n            return state.success.get()\n        }\n    } catch (ex: IOException) {\n        logger.error(\"PMTiles conversion failed\", ex)\n        return false\n    }\n}\n\nprivate fun updateMetadata(\n    oldMetadata: TileArchiveMetadata,\n    minZoom: Int,\n    maxZoom: Int,\n    targetCompressType: TileCompression?,\n): TileArchiveMetadata =\n    TileArchiveMetadata(\n        oldMetadata.name(),\n        oldMetadata.description(),\n        oldMetadata.attribution(),\n        oldMetadata.version(),\n        oldMetadata.type(),\n        TileFormat.MLT,\n        oldMetadata.bounds(),\n        oldMetadata.center(),\n        minZoom,\n        maxZoom,\n        oldMetadata.json(),\n        oldMetadata.others(),\n        targetCompressType,\n    )\n\nprivate fun processTiles(\n    tileSeq: Sequence<ReadablePmtiles.TileCoordRange>,\n    reader: ReadablePmtiles,\n    tileWriter: WriteableTileArchive.TileWriter,\n    taskRunner: TaskRunner,\n    config: EncodeConfig,\n    state: ConversionState,\n) {\n    tileSeq.forEach { tileRange ->\n        if (state.encodeConfig.continueOnError || state.success.get()) {\n            state.totalTileCount.addAndGet(tileRange.tileCount.toLong())\n            try {\n                taskRunner.run(\n                    {\n                        if (!processTileRange(tileRange, reader, tileWriter, config, state)) {\n                            state.success.set(false)\n                        }\n                    },\n                )\n            } catch (ignored: RejectedExecutionException) {\n                // This indicates the thread pool has been shut down due to an error\n            }\n        }\n    }\n    state.directoryComplete.set(true)\n    logger.debug(\n        \"Directory read complete. Processing {} total tiles.\",\n        String.format(\"%,d\", state.totalTileCount.get()),\n    )\n}\n\n/** Convert a range of tile coordinates which have the same contents */\nprivate fun processTileRange(\n    tileRef: ReadablePmtiles.TileCoordRange,\n    reader: ReadablePmtiles,\n    writer: WriteableTileArchive.TileWriter,\n    config: EncodeConfig,\n    state: ConversionState,\n): Boolean {\n    val tileCoords = tileRef.tileCoords\n    val firstTileCoord = tileCoords.first()\n    val tileLabel = getTileLabel(firstTileCoord)\n    val prevTileCount = state.tilesProcessed.getAndAdd(tileCoords.size.toLong())\n    val curTileCount = prevTileCount + tileCoords.size\n\n    // Log every Nth tile at debug level\n    if (logger.isDebugEnabled) {\n        val prevBatch = prevTileCount / tileLogInterval\n        val curBatch = curTileCount / tileLogInterval\n        // If we're the range that crosses a log interval boundary, log the progress.\n        if (curBatch != prevBatch || (state.directoryComplete.get() && curTileCount == state.totalTileCount.get())) {\n            if (!state.directoryComplete.get()) {\n                // Still fetching tile coordinates, we can't show a percentage.\n                logger.debug(\n                    \"Converted {} unique of {} tiles : {}\",\n                    String.format(\"%,d\", state.tilesConverted.get()),\n                    String.format(\"%,d\", curTileCount),\n                    tileLabel,\n                )\n            } else {\n                val totalTiles = state.totalTileCount.get()\n                logger.debug(\n                    \"Converted {} unique of {} encountered of {} total tiles ({}%) : {}\",\n                    String.format(\"%,d\", state.tilesConverted.get()),\n                    String.format(\"%,d\", curTileCount),\n                    String.format(\"%,d\", totalTiles),\n                    String.format(\"%.1f\", 100.0 * curTileCount / totalTiles),\n                    tileLabel,\n                )\n            }\n            if (config.logCacheStats) {\n                rangeReaderCache.get()?.also(::logCacheStats)\n            }\n        }\n    }\n\n    if (tileRef.byteRange.length <= maxTileTrackSize) {\n        state.offsetToHashMap.get(tileRef.byteRange.offset)?.let { tileHash ->\n            // Already processed this input tile for a different tile coordinate range.\n            // Pass an empty result with the same hash to the writer.  Rely on\n            // `WritablePmtiles.DeduplicatingTileArchiveWriter` to locate the previous entry and\n            // never write this empty result, though it provides no feedback we can use to check.\n            writeTiles(ArrayUtils.EMPTY_BYTE_ARRAY, OptionalLong.of(tileHash), tileCoords, writer)\n            // nothing more to do for this tile range\n            return@processTileRange true\n        }\n    }\n\n    // Get the raw tile contents\n    var tileData = reader.getBytes(tileRef.byteRange)\n\n    // Decompress the raw tile data\n    val compressedMVTSize = tileData.size\n    try {\n        tileData = decompress(ByteArrayInputStream(tileData))\n    } catch (ex: IOException) {\n        logger.error(\"Failed to decompress tile {}\", tileLabel, ex)\n        return false\n    }\n\n    val uncompressedMVTSize = tileData.size\n    if (logger.isTraceEnabled) {\n        val comp =\n            if (compressedMVTSize != uncompressedMVTSize) {\n                \", $compressedMVTSize compressed\"\n            } else {\n                \"\"\n            }\n        logger.trace(\n            readMarker,\n            \"{} loaded, {} bytes{}\",\n            tileLabel,\n            uncompressedMVTSize,\n            comp,\n        )\n    }\n\n    // Convert to MLT and optionally re-compress\n    val didCompress = MutableBoolean()\n    val mltData =\n        try {\n            convertTile(\n                firstTileCoord.x().toLong(),\n                firstTileCoord.y().toLong(),\n                firstTileCoord.z(),\n                tileData,\n                state.encodeConfig,\n                Optional.empty<Double>(), // compress even if it increases the size\n                Optional.empty<Long>(),\n                didCompress,\n            )\n        } catch (ex: Exception) {\n            logger.error(\n                \"Error processing tile range {} +{}\",\n                getTileLabel(firstTileCoord),\n                tileCoords.size - 1,\n                ex,\n            )\n            state.success.set(false)\n            return false\n        }\n    state.tilesConverted.incrementAndGet()\n\n    // Write the result for all the tile coordinates in the range\n    if (mltData != null && mltData.size > 0) {\n        val hash = OptionalLong.of(TileArchiveWriter.generateContentHash(mltData))\n\n        val existingHash =\n            if (tileRef.byteRange.length <= maxTileTrackSize) {\n                state.offsetToHashMap.putIfAbsent(tileRef.byteRange.offset, hash.asLong)\n            } else {\n                null\n            }\n        if (existingHash != null) {\n            // The value was already present.  This indicates that another thread processed the\n            // same input tile since we checked above, and this thread's result will be ignored.\n            if (hash.asLong == existingHash) {\n                // As above, we write with no data and the same hash to reference the previous result\n                writeTiles(ArrayUtils.EMPTY_BYTE_ARRAY, hash, tileCoords, writer)\n                return@processTileRange true\n            } else {\n                logger.warn(\n                    \"Hash collision for {} : {} != {}. This will cause incorrect de-duplication.\",\n                    tileLabel,\n                    existingHash,\n                    hash.asLong,\n                )\n                return@processTileRange false\n            }\n        }\n\n        writeTiles(mltData, hash, tileCoords, writer)\n\n        if (logger.isTraceEnabled) {\n            val extraCoords =\n                if (tileCoords.size > 1) {\n                    \"(+ ${tileCoords.size - 1}: ${getTileLabels(tileCoords.drop(1), 10)})\"\n                } else {\n                    \"\"\n                }\n            logger.trace(\n                writeMarker,\n                \"Writing {}{}, {} bytes{} hash {}\",\n                tileLabel,\n                extraCoords,\n                mltData.size,\n                if (didCompress.isTrue) \" (compressed)\" else \"\",\n                hash.asLong,\n            )\n        }\n    } else {\n        logger.warn(\"Tile {} produced empty output, skipping\", tileLabel)\n    }\n    return true\n}\n\nprivate fun writeTiles(\n    tileData: ByteArray,\n    tileHash: OptionalLong,\n    tileCoords: Collection<TileCoord>,\n    writer: WriteableTileArchive.TileWriter,\n) = synchronized(writer) {\n    // The writer is not thread-safe\n    tileCoords.forEach { tileCoord ->\n        writer.write(TileEncodingResult(tileCoord, tileData, tileHash))\n    }\n}\n\nprivate fun toTileCompression(compression: Pmtiles.Compression): TileCompression =\n    when (compression) {\n        Pmtiles.Compression.NONE -> TileCompression.NONE\n        Pmtiles.Compression.GZIP -> TileCompression.GZIP\n        else -> throw IllegalArgumentException(\"Unsupported compression type: $compression\")\n    }\n\nprivate fun toCompressionOption(compression: TileCompression): String? =\n    when (compression) {\n        TileCompression.NONE -> null\n        TileCompression.GZIP -> \"gzip\"\n        else -> throw IllegalArgumentException(\"Unsupported compression type: $compression\")\n    }\n\nprivate data class ConversionState(\n    // Options passed to each tile conversion\n    val encodeConfig: EncodeConfig,\n    // Used to track tile hashes to identify repeated tiles in separate tile ranges.\n    // Maps from the byte offset in the source file to the hash of the generated contents.\n    val offsetToHashMap: ConcurrentHashMap<Long, Long> = ConcurrentHashMap(),\n    // The number of tiles processed so far\n    val tilesProcessed: AtomicLong = AtomicLong(0),\n    val tilesConverted: AtomicLong = AtomicLong(0),\n    // The total number of tiles seen in the directory so far\n    val totalTileCount: AtomicLong = AtomicLong(0),\n    // Whether we've finished reading the directory\n    val directoryComplete: AtomicBoolean = AtomicBoolean(false),\n    // Whether the conversion is successful so far\n    val success: AtomicBoolean = AtomicBoolean(true),\n)\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/ReadablePmtiles.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport com.onthegomap.planetiler.archive.TileArchiveMetadata\nimport com.onthegomap.planetiler.archive.TileCompression\nimport com.onthegomap.planetiler.archive.TileFormat\nimport com.onthegomap.planetiler.geo.TileCoord\nimport com.onthegomap.planetiler.pmtiles.Pmtiles\nimport com.onthegomap.planetiler.util.Gzip\nimport org.locationtech.jts.geom.Coordinate\nimport java.io.IOException\nimport java.io.UncheckedIOException\n\n/** Potentially thread-safe partial copy of Planetiler's Pmtiles reader.\n * This class is thread-safe as long as the provided DataReader is.\n * Also exposes run-length encoded tile coordinate ranges\n * which are not currently exposed by Planetiler's API. */\nclass ReadablePmtiles(\n    private val channel: DataReader,\n    private val closeSourceChannel: Boolean = true,\n) : AutoCloseable {\n    data class ByteRange(\n        /** The starting position of the range */\n        val offset: Long,\n        /** The number of bytes in the range */\n        val length: Int,\n    ) {\n        init {\n            if (offset < 0) {\n                throw IllegalArgumentException(\"ByteRange offset must be non-negative\")\n            }\n            if (length < 1) {\n                throw IllegalArgumentException(\"ByteRange length must be positive\")\n            }\n        }\n\n        override fun equals(other: Any?) = this === other || (other is ByteRange && offset == other.offset && length == other.length)\n    }\n\n    interface DataReader : AutoCloseable {\n        fun read(\n            offset: Long,\n            length: Int,\n        ): ByteArray\n\n        override fun close() {}\n    }\n\n    private fun getBytes(\n        start: Long,\n        length: Long,\n    ) = channel.read(start, Math.toIntExact(length))\n\n    fun getBytes(range: ByteRange) = getBytes(range.offset, range.length.toLong())\n\n    private fun getHeaderBytes(\n        offset: Long,\n        length: Long,\n    ) = getBytes(offset, length).let {\n        if (header.internalCompression() == Pmtiles.Compression.GZIP) {\n            Gzip.gunzip(it)\n        } else {\n            it\n        }\n    }\n\n    fun getTileRange(\n        x: Int,\n        y: Int,\n        z: Int,\n    ): ByteRange? {\n        try {\n            val tileId = TileCoord.ofXYZ(x, y, z).hilbertEncoded()\n\n            var dirOffset = header.rootDirOffset()\n            var dirLength = header.rootDirLength().toInt()\n\n            for (depth in 0..3) {\n                val dirBytes = getHeaderBytes(dirOffset, dirLength.toLong())\n                val dir = Pmtiles.directoryFromBytes(dirBytes)\n                val entry = findTile(dir, tileId.toLong())\n                if (entry != null) {\n                    if (entry.runLength() > 0) {\n                        return ByteRange(header.tileDataOffset() + entry.offset(), entry.length())\n                    } else {\n                        dirOffset = header.leafDirectoriesOffset() + entry.offset()\n                        dirLength = entry.length()\n                    }\n                } else {\n                    return null\n                }\n            }\n        } catch (e: IOException) {\n            throw IllegalStateException(\"Could not get tile\", e)\n        }\n\n        return null\n    }\n\n    fun getTile(\n        x: Int,\n        y: Int,\n        z: Int,\n    ) = getTileRange(x, y, z)?.let(::getBytes)\n\n    override fun close() {\n        if (closeSourceChannel) {\n            channel.close()\n        }\n    }\n\n    val jsonMetadata by lazy {\n        Pmtiles.JsonMetadata.fromBytes(getHeaderBytes(header.jsonMetadataOffset, header.jsonMetadataLength))\n    }\n\n    fun metadata(): TileArchiveMetadata {\n        val tileCompression =\n            when (header.tileCompression) {\n                Pmtiles.Compression.GZIP -> TileCompression.GZIP\n                Pmtiles.Compression.NONE -> TileCompression.NONE\n                Pmtiles.Compression.UNKNOWN -> TileCompression.UNKNOWN\n            }\n\n        val format =\n            when (header.tileType) {\n                Pmtiles.TileType.MVT -> TileFormat.MVT\n                Pmtiles.TileType.MLT -> TileFormat.MLT\n                else -> null\n            }\n\n        try {\n            val jsonMetadata = this.jsonMetadata\n            val map = LinkedHashMap<String, String?>(jsonMetadata.otherMetadata)\n            return TileArchiveMetadata(\n                map.remove(TileArchiveMetadata.NAME_KEY),\n                map.remove(TileArchiveMetadata.DESCRIPTION_KEY),\n                map.remove(TileArchiveMetadata.ATTRIBUTION_KEY),\n                map.remove(TileArchiveMetadata.VERSION_KEY),\n                map.remove(TileArchiveMetadata.TYPE_KEY),\n                format,\n                header.bounds(),\n                Coordinate(\n                    header.center().getX(),\n                    header.center().getY(),\n                    header.centerZoom.toDouble(),\n                ),\n                header.minZoom.toInt(),\n                header.maxZoom.toInt(),\n                TileArchiveMetadata.TileArchiveMetadataJson.create(jsonMetadata.vectorLayers),\n                map,\n                tileCompression,\n            )\n        } catch (e: IOException) {\n            throw UncheckedIOException(e)\n        }\n    }\n\n    private fun readDir(entry: Pmtiles.Entry) = readDir(header.leafDirectoriesOffset() + entry.offset(), entry.length().toLong())\n\n    private fun readDir(\n        offset: Long,\n        length: Long,\n    ) = Pmtiles.directoryFromBytes(getHeaderBytes(offset, length))\n\n    data class TileCoordRange(\n        val startTileId: Long,\n        val tileCount: Int,\n        val byteRange: ByteRange,\n    ) {\n        constructor(entry: Pmtiles.Entry, header: Pmtiles.Header) : this(\n            entry.tileId(),\n            entry.runLength(),\n            ByteRange(header.tileDataOffset() + entry.offset(), entry.length()),\n        )\n\n        init {\n            if (tileCount < 1) {\n                throw IllegalArgumentException(\"tileCount must be positive\")\n            }\n            if (byteRange.offset < 0) {\n                throw IllegalArgumentException(\"byteRange offset must be non-negative\")\n            }\n            if (byteRange.length < 1) {\n                throw IllegalArgumentException(\"byteRange length must be positive\")\n            }\n        }\n\n        val tileIds get() = (startTileId until startTileId + tileCount)\n\n        // Warning: this will only work on z15 or less pmtiles which planetiler creates\n        // TODO: Will extending `hilbertDecode` to Longs solve this?\n        val tileCoords get() = tileIds.map { TileCoord.hilbertDecode(it.toInt()) }\n    }\n\n    fun getTileCoordRanges(\n        minZoom: Int? = null,\n        maxZoom: Int? = null,\n    ) = getTileCoordRanges(rootDir, minZoom?.let(::zoomStartIndex) ?: 0, maxZoom?.let(::zoomEndIndex) ?: Long.MAX_VALUE)\n\n    /** Generate tile ranges for the Hilbert given tile indexes (half-closed range)\n     * @param dir the directory to search\n     * @param startTileIndex the first tile index to include\n     * @param endTileIndex the first tile index to exclude\n     * */\n    private fun getTileCoordRanges(\n        dir: Iterable<Pmtiles.Entry>,\n        startTileIndex: Long,\n        endTileIndex: Long,\n    ): Sequence<TileCoordRange> =\n        dir\n            .asSequence()\n            .flatMap<Pmtiles.Entry, TileCoordRange> { entry ->\n                mapDirectory(entry, startTileIndex, endTileIndex, header) {\n                    getTileCoordRanges(readDir(entry), startTileIndex, endTileIndex)\n                }\n            }\n\n    /** Zoom start index, sum of previous tile counts */\n    private fun zoomStartIndex(zoom: Int) = ZOOM_START_INDEX[zoom.coerceIn(0..MAX_ZOOM)]\n\n    /** Zoom end index, first index of the next zoom */\n    private fun zoomEndIndex(zoom: Int) = ZOOM_START_INDEX[zoom.coerceIn(0..MAX_ZOOM) + 1]\n\n    val header by lazy { Pmtiles.Header.fromBytes(getBytes(0, HEADER_LEN.toLong())) }\n\n    private val rootDir by lazy { readDir(header.rootDirOffset(), header.rootDirLength()) }\n\n    companion object {\n        const val HEADER_LEN: Int = 127 // Pmtiles.HEADER_LEN is inaccessible\n\n        const val MAX_ZOOM = 16 // Planetiler currently only supports 15\n\n        private val ZOOM_START_INDEX = buildZoomIndex()\n\n        private fun buildZoomIndex(): LongArray {\n            val indexes = LongArray(MAX_ZOOM + 2)\n            var index = 0L\n            (0..MAX_ZOOM + 1).forEach {\n                indexes[it] = index\n                val width = (1L shl it)\n                index += width * width\n                if (index < indexes[it]) {\n                    throw IllegalStateException(\"Too many zoom levels\")\n                }\n            }\n            return indexes\n        }\n\n        /**\n         * Finds the relevant entry for a tileId in a list of entries.\n         *\n         * If there is an exact match for tileId, return that. Else if the tileId matches an entry's\n         * tileId + runLength, return that. Else if the preceding entry is a directory (runLength = 0),\n         * return that. Else return null.\n         */\n        private fun findTile(\n            entries: List<Pmtiles.Entry>,\n            tileId: Long,\n        ): Pmtiles.Entry? {\n            var m = 0\n            var n = entries.size - 1\n            while (m <= n) {\n                val k = (n + m) shr 1\n                val entry = entries.get(k)\n                val cmp = tileId - entry.tileId()\n                if (cmp > 0) {\n                    m = k + 1\n                } else if (cmp < 0) {\n                    n = k - 1\n                } else {\n                    return entry\n                }\n            }\n            if (n >= 0) {\n                val entry = entries.get(n)\n                if ((entry.runLength() == 0 || tileId - entry.tileId() < entry.runLength())) {\n                    return entry\n                }\n            }\n            return null\n        }\n\n        internal fun mapDirectory(\n            entry: Pmtiles.Entry,\n            startTileIndex: Long,\n            endTileIndex: Long,\n            header: Pmtiles.Header,\n            recurse: (entry: Pmtiles.Entry) -> Sequence<TileCoordRange>,\n        ): Sequence<TileCoordRange> {\n            // Run-length zero is a directory reference\n            if (entry.runLength() == 0) {\n                // continue exploring this branch?\n                if (entry.tileId() < endTileIndex) {\n                    return recurse(entry)\n                }\n            } else {\n                // Run-length non-zero is a tile range\n                var rangeEnd = entry.tileId() + entry.runLength()\n                if (startTileIndex <= entry.tileId() && rangeEnd <= endTileIndex) {\n                    // use the full range\n                    return sequenceOf(TileCoordRange(entry, header))\n                } else if (entry.tileId() < endTileIndex && startTileIndex < rangeEnd) {\n                    // use a partial range\n                    val start = entry.tileId().coerceAtLeast(startTileIndex)\n                    val end = (entry.tileId() + entry.runLength()).coerceAtMost(endTileIndex)\n                    return sequenceOf(\n                        TileCoordRange(\n                            start,\n                            (end - start).toInt(),\n                            ByteRange(header.tileDataOffset() + entry.offset(), entry.length()),\n                        ),\n                    )\n                }\n            }\n            return sequenceOf()\n        }\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/SerialTaskRunner.kt",
    "content": "package org.maplibre.mlt.cli\n\nclass SerialTaskRunner : TaskRunner {\n    override val threadCount: Int\n        get() = 0\n\n    override fun run(task: Runnable?) {\n        if (task != null) {\n            task.run()\n        }\n    }\n\n    override fun awaitTermination() {\n    }\n\n    override fun shutdown() {}\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Server.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.slf4j.Logger\nimport org.slf4j.LoggerFactory\nimport java.io.BufferedReader\nimport java.io.InputStreamReader\nimport java.net.ServerSocket\nimport java.net.Socket\n\nclass Server {\n    fun run(port: Int) {\n        if (isRunning(port)) {\n            throw RuntimeException(\"Port $port is already in use\")\n        }\n\n        startServer(port) // never returns\n    }\n\n    private fun isRunning(port: Int): Boolean {\n        try {\n            Socket(\"localhost\", port).use { _ ->\n                return true\n            }\n        } catch (_: Exception) {\n            return false\n        }\n    }\n\n    private fun startServer(port: Int) {\n        try {\n            ServerSocket(port).use { server ->\n                logger.info(\"Server started on port {}\", port)\n                while (true) {\n                    val client = server.accept()\n                    Thread(Runnable { handleClient(client) }).start()\n                }\n            }\n        } catch (ex: Exception) {\n            logger.error(\"Failed to start server on port {}\", port, ex)\n        }\n    }\n\n    private fun handleClient(socket: Socket) {\n        try {\n            BufferedReader(InputStreamReader(socket.getInputStream())).use { `in` ->\n                val command = `in`.readLine()\n                if (command != null) {\n                    Encode.run(\n                        command\n                            .trim { it <= ' ' }\n                            .split(\"\\\\s+\".toRegex())\n                            .dropLastWhile { it.isEmpty() }\n                            .toTypedArray(),\n                    )\n                }\n            }\n        } catch (ex: Exception) {\n            logger.error(\"Failed to handle client connection\", ex)\n        }\n    }\n\n    companion object {\n        private val logger: Logger = LoggerFactory.getLogger(Server::class.java)\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/TaskRunner.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\n\n/**  Simplify optional parallel operation by running tasks in a thread pool if provided, or directly if not. */\ninterface TaskRunner {\n    /** Get the number of threads in use, not including the main thread */\n    val threadCount: Int\n\n    /**  Execute the given task either directly or on the given thread pool */\n    fun run(task: Runnable?)\n\n    /**  Wait for all tasks to complete.  Assumes shutdown has been called. */\n    fun awaitTermination()\n\n    /**  Stop accepting new tasks */\n    fun shutdown()\n}\n\nfun createBoundedNonRejectingTaskRunner(threadCount: Int): TaskRunner {\n    if (threadCount < 2) {\n        return SerialTaskRunner()\n    }\n    // Because the main thread is also used for running tasks when the pool is saturated,\n    // we count it as one of the threads and so reduce the pool size by one.\n    val poolSize = threadCount - 1\n    // Threads are expected to be used continuously, and so don't time out.\n    val threadTimeout = Long.MAX_VALUE\n    // Create a thread pool with a bounded task queue that will not reject tasks when\n    // it's full.  Tasks beyond the limit will run on the calling thread, preventing\n    // OOM from too many tasks while allowing for parallelism when the pool is available.\n    val taskQueue = LinkedBlockingQueue<Runnable>(threadQueueSize * poolSize)\n    val rejectHandler = ThreadPoolExecutor.CallerRunsPolicy()\n    return ThreadPoolTaskRunner(\n        ThreadPoolExecutor(\n            poolSize,\n            poolSize,\n            threadTimeout,\n            TimeUnit.SECONDS,\n            taskQueue,\n            rejectHandler,\n        ),\n    )\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/ThreadPoolTaskRunner.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\n\nclass ThreadPoolTaskRunner(\n    private val threadPool: ThreadPoolExecutor,\n) : TaskRunner {\n    override val threadCount: Int\n        get() = threadPool.maximumPoolSize\n\n    override fun run(task: Runnable?) {\n        if (task != null) {\n            threadPool.execute(task)\n        }\n    }\n\n    override fun awaitTermination() {\n        threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)\n    }\n\n    override fun shutdown() {\n        threadPool.shutdown()\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/kotlin/org/maplibre/mlt/cli/Timer.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.slf4j.LoggerFactory\n\nclass Timer {\n    private var startTime: Long\n\n    init {\n        startTime = System.nanoTime()\n    }\n\n    fun restart() {\n        startTime = System.nanoTime()\n    }\n\n    val elapsedTime get() = (System.nanoTime() - startTime).toDouble() / 1_000_000 // divide by 1000000 to get milliseconds\n\n    fun stop(message: String?) {\n        logger.info(\"Time elapsed for {}: {} ms\", message, elapsedTime)\n    }\n\n    private val logger = LoggerFactory.getLogger(Timer::class.java)\n}\n"
  },
  {
    "path": "java/mlt-cli/src/main/resources/log4j2.json",
    "content": "{\n  \"Configuration\": {\n    \"status\": \"warn\",\n    \"Appenders\": {\n      \"Console\": {\n        \"name\": \"Console\",\n         \"target\": \"system_err\",\n        \"PatternLayout\": {\n          \"noConsoleNoAnsi\": true,\n          \"LevelPatternSelector\": {\n            \"PatternMatch\": [\n              {\n                \"key\": \"FATAL\",\n                \"pattern\": \"%highlight{FATAL: %msg%n%throwable}{FATAL=bright red}\"\n              },\n              {\n                \"key\": \"ERROR\",\n                \"pattern\": \"%highlight{ERROR: %msg%n%throwable}{ERROR=red}\"\n              },\n              {\n                \"key\": \"WARN\",\n                \"pattern\": \"%highlight{WARNING: %msg%n%throwable}{WARN=yellow}\"\n              },\n              {\n                \"key\": \"INFO\",\n                \"pattern\": \"%msg%n%throwable\"\n              },\n              {\n                \"key\": \"DEBUG\",\n                \"pattern\": \"%highlight{%msg%n%throwable}{DEBUG=dim white}\"\n              },\n              {\n                \"key\": \"TRACE\",\n                \"pattern\": \"%highlight{%msg%n%throwable}{TRACE=bright black}\"\n              }\n            ]\n          }\n        }\n      }\n    },\n    \"Loggers\": {\n      \"Root\": {\n        \"AppenderRef\": [\n          {\n            \"ref\": \"Console\"\n          }\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/EncodeCommandLineTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.commons.cli.DefaultParser\nimport org.apache.commons.cli.Option\nimport org.apache.commons.cli.Options\nimport org.apache.logging.log4j.Level\nimport org.junit.jupiter.api.Assertions.assertArrayEquals\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertNotNull\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\n\nclass EncodeCommandLineTest {\n    @Test\n    fun `getColumnMappings list`() {\n        val options = Options()\n        options.addOption(\n            Option\n                .builder()\n                .longOpt(EncodeCommandLine.COLUMN_MAPPING_LIST_OPTION)\n                .hasArg(true)\n                .argName(\"map\")\n                .get(),\n        )\n        val args = arrayOf(\"--${EncodeCommandLine.COLUMN_MAPPING_LIST_OPTION}\", \"[]name,name:de\")\n        val cmd = DefaultParser().parse(options, args)\n        val cfg = EncodeCommandLine.getColumnMappings(cmd)\n\n        assertEquals(1, cfg.size)\n        val entry = cfg.entries.first()\n        // empty layer list ([]) should match all\n        assertEquals(\".*\", entry.key.pattern())\n        val mappings = entry.value\n        assertEquals(1, mappings.size)\n        val m = mappings[0]\n        assertTrue(m.hasColumnNames())\n        assertTrue(m.columnNames.contains(\"name\"))\n        assertTrue(m.columnNames.contains(\"name:de\"))\n    }\n\n    @Test\n    fun `getColumnMappings delimited`() {\n        val options = Options()\n        options.addOption(\n            Option\n                .builder()\n                .longOpt(EncodeCommandLine.COLUMN_MAPPING_DELIM_OPTION)\n                .hasArg(true)\n                .argName(\"map\")\n                .get(),\n        )\n        val args = arrayOf(\"--${EncodeCommandLine.COLUMN_MAPPING_DELIM_OPTION}\", \"[roads]name/[:_]/\")\n        val cmd = DefaultParser().parse(options, args)\n        val cfg = EncodeCommandLine.getColumnMappings(cmd)\n\n        assertEquals(1, cfg.size)\n        val entry = cfg.entries.first()\n        // layer discriminator should create a literal pattern for 'roads'\n        assertEquals(\"roads\", entry.key.pattern())\n        val m = entry.value[0]\n        assertFalse(m.hasColumnNames())\n        assertEquals(\"name\", m.prefix.pattern())\n        assertEquals(\"[:_]\", m.delimiter.pattern())\n    }\n\n    @Test\n    fun `getAllowedCompressions Pmtiles`() {\n        val options =\n            Options().addOption(\n                Option\n                    .builder()\n                    .longOpt(EncodeCommandLine.INPUT_PMTILES_ARG)\n                    .hasArg(true)\n                    .get(),\n            )\n        val cmd = DefaultParser().parse(options, arrayOf(\"--${EncodeCommandLine.INPUT_PMTILES_ARG}\", \"input.pmtiles\"))\n        val list = EncodeCommandLine.getAllowedCompressions(cmd).toList()\n        // should contain the standard compressors plus brotli/zstd when pmtiles option present\n        assertTrue(list.containsAll(listOf(\"gzip\", \"deflate\", \"none\", \"brotli\", \"zstd\")))\n    }\n\n    @Test\n    fun `getAllowedCompressions not Pmtiles`() {\n        val cmd = DefaultParser().parse(Options(), arrayOf<String>())\n        val list = EncodeCommandLine.getAllowedCompressions(cmd).toList()\n        assertTrue(list.containsAll(listOf(\"gzip\", \"deflate\", \"none\")))\n        assertFalse(list.contains(\"brotli\") || list.contains(\"zstd\"))\n    }\n\n    /** Tests for the helper extension properties on `CommandLine` defined in `EncodeCommandLine.kt` */\n    @Test\n    fun `basic parsing of helper properties`() {\n        val args =\n            arrayOf(\n                \"--mvt\",\n                \"input.mvt\",\n                \"--minzoom\",\n                \"2\",\n                \"--maxzoom\",\n                \"10\",\n                \"--parallel\",\n                \"3\",\n                \"--verbose\",\n                \"debug\",\n                \"--sort-ids\",\n                \"^foo$\",\n                \"--regen-ids\",\n                \"bar\",\n                \"--outlines\",\n                \"one,two\",\n                \"--enable-fsst\",\n                \"--enable-fsst-native\",\n                \"--tessellateurl\",\n                \"http://example/\",\n                \"--compress\",\n                \"gzip\",\n                \"--coerce-mismatch\",\n                \"--elide-mismatch\",\n                \"--filter-layers\",\n                \"name.*\",\n                \"--filter-layers-invert\",\n            )\n\n        val cmd = EncodeCommandLine.getCommandLine(args)\n        assertNotNull(cmd)\n        cmd!!\n\n        assertEquals(2, cmd.minZoom)\n        assertEquals(10, cmd.maxZoom)\n        assertEquals(3, cmd.threadCount)\n        assertEquals(Level.DEBUG, cmd.logLevel)\n\n        assertEquals(\"^foo$\", cmd.sortFeaturesPattern?.pattern())\n        assertEquals(\"bar\", cmd.regenIDsPattern?.pattern())\n\n        val outlines = cmd.outlineFeatureTables\n        assertNotNull(outlines)\n        assertArrayEquals(arrayOf(\"one\", \"two\"), outlines)\n\n        assertTrue(cmd.useFSSTJava)\n        assertTrue(cmd.useFSSTNative)\n\n        assertEquals(\"http://example/\", cmd.tessellateSource)\n        assertTrue(cmd.tessellatePolygons)\n\n        assertEquals(\"gzip\", cmd.compressionType)\n\n        assertTrue(cmd.enableCoerceOnTypeMismatch)\n        assertTrue(cmd.enableElideOnTypeMismatch)\n\n        assertEquals(\"name.*\", cmd.filterRegex)\n        assertEquals(\"name.*\", cmd.filterPattern?.pattern())\n        assertTrue(cmd.filterInvert)\n    }\n\n    @Test\n    fun `cache stats forces debug log level`() {\n        val args =\n            arrayOf(\n                \"--\" + EncodeCommandLine.INPUT_PMTILES_ARG,\n                \"input.pmtiles\",\n                \"--\" + EncodeCommandLine.CACHE_STATS_OPTION,\n                \"--\" + EncodeCommandLine.VERBOSE_OPTION,\n                \"error\",\n            )\n        val cmd = EncodeCommandLine.getCommandLine(args)\n        assertNotNull(cmd)\n        cmd!!\n        assertEquals(Level.DEBUG, cmd.logLevel)\n    }\n\n    @Test\n    fun `log level isn't forced otherwise`() {\n        val args = arrayOf(\"--\" + EncodeCommandLine.INPUT_PMTILES_ARG, \"input.pmtiles\", \"--\" + EncodeCommandLine.VERBOSE_OPTION, \"error\")\n        val cmd = EncodeCommandLine.getCommandLine(args)\n        assertNotNull(cmd)\n        cmd!!\n        assertEquals(Level.ERROR, cmd.logLevel)\n    }\n\n    @Test\n    fun `thread count default when not present`() {\n        val args = arrayOf(\"--mvt\", \"input.mvt\")\n        val cmd = EncodeCommandLine.getCommandLine(args)\n        assertNotNull(cmd)\n        cmd!!\n        assertEquals(1, cmd.threadCount)\n    }\n\n    @Test\n    fun `thread count with explicit value`() {\n        val args = arrayOf(\"--mvt\", \"input.mvt\", \"--parallel\", \"2\")\n        val cmd = EncodeCommandLine.getCommandLine(args)\n        assertNotNull(cmd)\n        cmd!!\n        assertEquals(2, cmd.threadCount)\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/EnvironmentResolverTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.apache.logging.log4j.Level\nimport org.apache.logging.log4j.core.config.Configurator\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport kotlin.time.Duration.Companion.seconds\n\nclass EnvironmentResolverTest {\n    var savedResolver = envResolver\n\n    @BeforeEach\n    fun setUp() {\n        savedResolver = envResolver\n        Configurator.setLevel(logger.name, Level.ERROR) // suppress expected warning logs\n    }\n\n    @AfterEach\n    fun tearDown() {\n        envResolver = savedResolver\n    }\n\n    @Test\n    fun `override compression ratio with valid value`() {\n        envResolver = { name -> if (name == ENV_COMPRESSION_RATIO_THRESHOLD) \"0.5\" else null }\n        assertEquals(0.5, computeCompressionRatioThreshold())\n    }\n\n    @Test\n    fun `override compression ratio with invalid negative throws`() {\n        envResolver = { name -> if (name == ENV_COMPRESSION_RATIO_THRESHOLD) \"-1\" else null }\n        assertThrows(illegalArgType) {\n            computeCompressionRatioThreshold()\n        }.andContains(\"non-negative\")\n    }\n\n    /** When the parser throws (invalid format), resolveConfigValue should return the default */\n    @Test\n    fun `parser throws returns default`() {\n        Configurator.setLevel(logger.name, Level.ERROR) // suppress expected warning logs;\n        envResolver = { name -> if (name == ENV_COMPRESSION_RATIO_THRESHOLD) \"not-a-number\" else null }\n        assertEquals(DEFAULT_COMPRESSION_RATIO_THRESHOLD, computeCompressionRatioThreshold())\n    }\n\n    @Test\n    fun `computeCompressionFixedThreshold success`() {\n        envResolver = { name -> if (name == ENV_COMPRESSION_FIXED_THRESHOLD) \"123\" else null }\n        assertEquals(123L, computeCompressionFixedThreshold())\n    }\n\n    @Test\n    fun `computeCompressionFixedThreshold parser failure returns default`() {\n        envResolver = { name -> if (name == ENV_COMPRESSION_FIXED_THRESHOLD) \"bad\" else null }\n        assertEquals(DEFAULT_COMPRESSION_FIXED_THRESHOLD, computeCompressionFixedThreshold())\n    }\n\n    @Test\n    fun `computeTileLogInterval success`() {\n        envResolver = { name -> if (name == ENV_TILE_LOG_INTERVAL) \"12345\" else null }\n        assertEquals(12345L, computeTileLogInterval())\n    }\n\n    @Test\n    fun `computeTileLogInterval parser failure returns default`() {\n        envResolver = { name -> if (name == ENV_TILE_LOG_INTERVAL) \"bad\" else null }\n        assertEquals(DEFAULT_TILE_LOG_INTERVAL, computeTileLogInterval())\n    }\n\n    @Test\n    fun `computeTileLogInterval zero treated as never`() {\n        envResolver = { name -> if (name == ENV_TILE_LOG_INTERVAL) \"0\" else null }\n        assertEquals(Long.MAX_VALUE, computeTileLogInterval())\n    }\n\n    @Test\n    fun `cache expire accepts ISO duration`() {\n        envResolver = { name -> if (name == ENV_CACHE_EXPIRE) \"PT20S\" else null }\n        assertEquals(20.seconds, computeCacheExpireAfterAccess())\n    }\n\n    @Test\n    fun `cache expire negative throws`() {\n        envResolver = { name -> if (name == ENV_CACHE_EXPIRE) \"-PT1S\" else null }\n        assertThrows(illegalArgType) { computeCacheExpireAfterAccess() }.andContains(\"non-negative\")\n    }\n\n    @Test\n    fun `thread queue size positive`() {\n        envResolver = { name -> if (name == ENV_THREAD_QUEUE_SIZE) \"5\" else null }\n        assertEquals(5, computeThreadQueueSize())\n    }\n\n    @Test\n    fun `thread queue size invalid throws`() {\n        envResolver = { name -> if (name == ENV_THREAD_QUEUE_SIZE) \"0\" else null }\n        assertThrows(illegalArgType) { computeThreadQueueSize() }.andContains(\"positive\")\n    }\n\n    @Test\n    fun `cache max heap valid`() {\n        envResolver = { name -> if (name == ENV_CACHE_MAX) \"12345\" else null }\n        assertEquals(12345L, computeCacheMaxSize())\n    }\n\n    @Test\n    fun `cache max zero throws`() {\n        envResolver = { name -> if (name == ENV_CACHE_MAX) \"0\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxSize() }.andContains(\"positive\")\n    }\n\n    @Test\n    fun `cache max negative throws`() {\n        // larger than Long.MAX_VALUE\n        envResolver = { name -> if (name == ENV_CACHE_MAX) \"-1\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxSize() }.andContains(\"positive\")\n    }\n\n    @Test\n    fun `cache max heap percent valid range accepts valid`() {\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"99.9\" else null }\n        assertEquals(99.9, computeCacheMaxHeapPercent())\n    }\n\n    @Test\n    fun `cache max heap percent invalid throws`() {\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"-1\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxHeapPercent() }.andContains(\"between 0 and 100\")\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"100\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxHeapPercent() }.andContains(\"between 0 and 100\")\n    }\n\n    @Test\n    fun `cache max heap percent detects special values`() {\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"NaN\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxHeapPercent() }.andContains(\"between 0 and 100\")\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"Infinity\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxHeapPercent() }.andContains(\"between 0 and 100\")\n        envResolver = { name -> if (name == ENV_CACHE_MAX_HEAP_PERCENT) \"-Infinity\" else null }\n        assertThrows(illegalArgType) { computeCacheMaxHeapPercent() }.andContains(\"between 0 and 100\")\n    }\n\n    @Test\n    fun `computeCacheAverageEntrySize success`() {\n        envResolver = { name -> if (name == ENV_CACHE_AVERAGE_WEIGHT) \"2048\" else null }\n        assertEquals(2048, computeCacheAverageEntrySize())\n    }\n\n    @Test\n    fun `computeCacheAverageEntrySize parser failure returns default`() {\n        envResolver = { name -> if (name == ENV_CACHE_AVERAGE_WEIGHT) \"x\" else null }\n        assertEquals(DEFAULT_CACHE_AVERAGE_WEIGHT, computeCacheAverageEntrySize())\n    }\n\n    @Test\n    fun `computeCacheAverageEntrySize negative throws`() {\n        envResolver = { name -> if (name == ENV_CACHE_AVERAGE_WEIGHT) \"-1\" else null }\n        assertThrows(illegalArgType) { computeCacheAverageEntrySize() }.andContains(\"not be negative\")\n    }\n\n    @Test\n    fun `computeCacheBlockSize success`() {\n        envResolver = { name -> if (name == ENV_CACHE_BLOCK_SIZE) \"2048\" else null }\n        assertEquals(2048, computeCacheBlockSize())\n    }\n\n    @Test\n    fun `computeCacheBlockSize parser failure returns default`() {\n        envResolver = { name -> if (name == ENV_CACHE_BLOCK_SIZE) \"x\" else null }\n        assertEquals(DEFAULT_CACHE_BLOCK_SIZE, computeCacheBlockSize())\n    }\n\n    @Test\n    fun `computeCacheBlockSize negative throws`() {\n        envResolver = { name -> if (name == ENV_CACHE_BLOCK_SIZE) \"-1\" else null }\n        assertThrows(illegalArgType) { computeCacheBlockSize() }.andContains(\"not be negative\")\n    }\n\n    @Test\n    fun `maxTileTrackSize success`() {\n        envResolver = { name -> if (name == ENV_MAX_TILE_TRACK_SIZE) \"12345\" else null }\n        assertEquals(12345, computeMaxTileTrackSize())\n    }\n\n    @Test\n    fun `maxTileTrackSize parser failure returns default`() {\n        envResolver = { name -> if (name == ENV_MAX_TILE_TRACK_SIZE) \"x\" else null }\n        assertEquals(DEFAULT_MAX_TILE_TRACK_SIZE, computeMaxTileTrackSize())\n    }\n\n    @Test\n    fun `maxTileTrackSize accepts zero and negative`() {\n        envResolver = { name -> if (name == ENV_MAX_TILE_TRACK_SIZE) \"0\" else null }\n        assertEquals(0, computeMaxTileTrackSize())\n        envResolver = { name -> if (name == ENV_MAX_TILE_TRACK_SIZE) \"-1\" else null }\n        assertEquals(0, computeMaxTileTrackSize())\n    }\n\n    private val illegalArgType = IllegalArgumentException::class.java\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/LoggingTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass LoggingTest {\n    @Test\n    fun `formatSize returns zero for non-positive`() {\n        assertEquals(\"0\", formatSize(0))\n        assertEquals(\"0\", formatSize(-1))\n    }\n\n    @Test\n    fun `formatSize formats bytes correctly`() {\n        assertEquals(\"1 B\", formatSize(1))\n        assertEquals(\"1,023 B\", formatSize(1023))\n    }\n\n    @Test\n    fun `formatSize formats kibibytes and beyond`() {\n        assertEquals(\"1 kiB\", formatSize(1024))\n        assertEquals(\"1.5 kiB\", formatSize(1536))\n        assertEquals(\"1 MiB\", formatSize(1024L * 1024))\n        assertEquals(\"5 GiB\", formatSize(5L * 1024 * 1024 * 1024))\n    }\n\n    @Test\n    fun `formatNanosecDuration formats ns, us, ms, s and zero`() {\n        // nanoseconds\n        assertEquals(\"0.50ns\", formatNanosecDuration(0.5))\n        assertEquals(\"1.00us\", formatNanosecDuration(1e3))\n        assertEquals(\"1.00ms\", formatNanosecDuration(1e6))\n        assertEquals(\"1.00s\", formatNanosecDuration(1e9))\n        assertEquals(\"2.35s\", formatNanosecDuration(2.345e9))\n        assertEquals(\"0\", formatNanosecDuration(0.0))\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/ReadablePmtilesMapDirectoryTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport com.onthegomap.planetiler.pmtiles.Pmtiles\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.whenever\n\n/** Tests for directory traversal logic in `ReadablePmtiles` */\nclass ReadablePmtilesMapDirectoryTest {\n    private fun mockEntry(\n        tileId: Long,\n        runLength: Int,\n        offset: Long = 0,\n        length: Int = 1,\n    ): Pmtiles.Entry {\n        val entry = mock<Pmtiles.Entry>()\n        whenever(entry.tileId()).thenReturn(tileId)\n        whenever(entry.runLength()).thenReturn(runLength)\n        whenever(entry.offset()).thenReturn(offset)\n        whenever(entry.length()).thenReturn(length)\n        return entry\n    }\n\n    private fun mockHeader(tileDataOffset: Long = 0): Pmtiles.Header {\n        val header = mock<Pmtiles.Header>()\n        whenever(header.tileDataOffset()).thenReturn(tileDataOffset)\n        return header\n    }\n\n    @Test\n    fun `directory reference entry calls recurse when tileId less than endTileIndex`() {\n        val entry = mockEntry(tileId = 5, runLength = 0)\n        val header = mockHeader()\n        var recurseCalled = false\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = {\n            recurseCalled = true\n            emptySequence()\n        }\n        val result = ReadablePmtiles.mapDirectory(entry, 0, 10, header, recurse)\n        assertTrue(recurseCalled)\n        assertTrue(result.toList().isEmpty())\n    }\n\n    @Test\n    fun `directory reference entry does not call recurse when tileId not less than endTileIndex`() {\n        val entry = mockEntry(tileId = 10, runLength = 0)\n        val header = mockHeader()\n        var recurseCalled = false\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = {\n            recurseCalled = true\n            emptySequence()\n        }\n        val result = ReadablePmtiles.mapDirectory(entry, 0, 10, header, recurse)\n        assertFalse(recurseCalled)\n        assertTrue(result.toList().isEmpty())\n    }\n\n    @Test\n    fun `tile range entry returns full range when within bounds`() {\n        val entry = mockEntry(tileId = 2, runLength = 3, offset = 10, length = 5)\n        val header = mockHeader(tileDataOffset = 100)\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = { emptySequence() }\n        val result = ReadablePmtiles.mapDirectory(entry, 0, 10, header, recurse)\n        val ranges = result.toList()\n        assertEquals(1, ranges.size)\n        val range = ranges[0]\n        assertEquals(2, range.startTileId)\n        assertEquals(3, range.tileCount)\n        assertEquals(ReadablePmtiles.ByteRange(110, 5), range.byteRange)\n    }\n\n    @Test\n    fun `tile range entry returns partial range when overlapping bounds`() {\n        val entry = mockEntry(tileId = 5, runLength = 5, offset = 20, length = 10)\n        val header = mockHeader(tileDataOffset = 50)\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = { emptySequence() }\n        val result = ReadablePmtiles.mapDirectory(entry, 7, 12, header, recurse)\n        val ranges = result.toList()\n        assertEquals(1, ranges.size)\n        val range = ranges[0]\n        assertEquals(7, range.startTileId)\n        assertEquals(3, range.tileCount)\n        assertEquals(ReadablePmtiles.ByteRange(70, 10), range.byteRange)\n    }\n\n    @Test\n    fun `tile range entry returns empty when out of bounds below`() {\n        val entry = mockEntry(tileId = 20, runLength = 2, offset = 30, length = 5)\n        val header = mockHeader(tileDataOffset = 0)\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = { emptySequence() }\n        val result = ReadablePmtiles.mapDirectory(entry, 0, 20, header, recurse)\n        assertTrue(result.toList().isEmpty())\n    }\n\n    @Test\n    fun `tile range entry returns empty when out of bounds above`() {\n        val entry = mockEntry(tileId = 20, runLength = 2, offset = 30, length = 5)\n        val header = mockHeader(tileDataOffset = 0)\n        val recurse: (Pmtiles.Entry) -> Sequence<ReadablePmtiles.TileCoordRange> = { emptySequence() }\n        val result = ReadablePmtiles.mapDirectory(entry, 22, 25, header, recurse)\n        assertTrue(result.toList().isEmpty())\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/TaskRunnerTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertDoesNotThrow\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.atomic.AtomicBoolean\n\nclass TaskRunnerTest {\n    @Test\n    fun `serial runner executes task immediately when thread count is less than two`() {\n        val ran = AtomicBoolean(false)\n        val runner = createBoundedNonRejectingTaskRunner(1)\n        assertTrue(runner is SerialTaskRunner)\n\n        runner.run(Runnable { ran.set(true) })\n\n        assertTrue(ran.get())\n        assertEquals(0, runner.threadCount)\n    }\n\n    @Test\n    fun `thread pool runner reports pool size excluding the main thread`() {\n        val runner = createBoundedNonRejectingTaskRunner(4)\n        try {\n            assertEquals(3, runner.threadCount)\n        } finally {\n            runner.shutdown()\n            runner.awaitTermination()\n        }\n    }\n\n    // Ensure that tasks run in parallel\n    @Test\n    fun `thread pool runner executes submitted tasks`() {\n        val threadCount = 3\n        val runner = createBoundedNonRejectingTaskRunner(threadCount)\n        val startLatch = CountDownLatch(1)\n        val stopLatch = CountDownLatch(threadCount - 1)\n        val mainThread = Thread.currentThread().threadId()\n        try {\n            repeat(threadCount - 1) {\n                runner.run({\n                    assertTrue(Thread.currentThread().threadId() != mainThread)\n                    assertDoesNotThrow { startLatch.await(1, TimeUnit.SECONDS) }\n                    stopLatch.countDown()\n                })\n            }\n            // allow threads to start\n            startLatch.countDown()\n            // ensure that all tasks complete\n            assertTrue(stopLatch.await(1, TimeUnit.SECONDS))\n        } finally {\n            runner.shutdown()\n            runner.awaitTermination()\n        }\n    }\n\n    // Add more tasks than threads to ensure that the pool doesn't reject tasks\n    // when saturated.\n    // Ideally we would fill the pool before allowing tasks to complete so it's\n    // not sensitive to timing, but the main thread will execute a task at saturation\n    // resulting in a deadlock.\n    @Test\n    fun `thread pool runner doesn't reject tasks`() {\n        val threadCount = 3\n        val taskCount = threadCount * 10\n        val runner = createBoundedNonRejectingTaskRunner(threadCount)\n        val startLatch = CountDownLatch(1)\n        val stopLatch = CountDownLatch(taskCount)\n        val mainThread = Thread.currentThread().threadId()\n        try {\n            repeat(taskCount) {\n                runner.run({\n                    if (Thread.currentThread().threadId() == mainThread) {\n                        // don't wait, we would deadlock\n                    } else {\n                        assertDoesNotThrow { startLatch.await(1, TimeUnit.SECONDS) }\n                    }\n                    stopLatch.countDown()\n                })\n            }\n            // allow threads to start\n            startLatch.countDown()\n            // ensure that all tasks complete\n            assertTrue(stopLatch.await(1, TimeUnit.SECONDS))\n        } finally {\n            runner.shutdown()\n            runner.awaitTermination()\n        }\n    }\n}\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/TestUtil.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.junit.jupiter.api.Assertions.assertTrue\n\nfun Exception.andContains(str: String) =\n    assertTrue(this.message?.contains(str) ?: false, \"Expected exception message to contain '$str', but was '${this.message}'\")\n"
  },
  {
    "path": "java/mlt-cli/src/test/kotlin/org/maplibre/mlt/cli/TileCoordRangeTest.kt",
    "content": "package org.maplibre.mlt.cli\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertThrows\nimport org.junit.jupiter.api.Test\n\nclass TileCoordRangeTest {\n    @Test\n    fun `allows zero offset byte range`() {\n        val range = ReadablePmtiles.TileCoordRange(42L, 3, ReadablePmtiles.ByteRange(0, 7))\n        assertEquals(42L, range.startTileId)\n        assertEquals(3, range.tileCount)\n        assertEquals(0L, range.byteRange.offset)\n    }\n\n    @Test\n    fun `rejects negative offset byte range`() {\n        assertThrows(IllegalArgumentException::class.java) {\n            ReadablePmtiles.TileCoordRange(0L, 1, ReadablePmtiles.ByteRange(-1, 7))\n        }\n    }\n\n    @Test\n    fun `rejects non-positive tile count`() {\n        assertThrows(IllegalArgumentException::class.java) {\n            ReadablePmtiles.TileCoordRange(0L, 0, ReadablePmtiles.ByteRange(0, 7))\n        }\n    }\n}\n"
  },
  {
    "path": "java/mlt-core/build.gradle",
    "content": "plugins {\n    alias(libs.plugins.diffplug.spotless)\n    alias(libs.plugins.maven.publish)\n    alias(libs.plugins.me.champeau.jmh)\n    id 'jacoco'\n    id 'java-library'\n}\n\nrepositories {\n    mavenCentral()\n    maven {\n        url = 'https://maven.ecc.no/releases'\n    }\n}\n\ndependencies {\n    api(libs.javaFastPFOR)\n    implementation(libs.commons.lang3)\n    implementation(libs.earcut4j)\n    implementation(libs.gson)\n    implementation(libs.guava)\n    implementation(libs.hppc)\n    implementation(libs.jakarta.annotation.api)\n    implementation(libs.java.vector.tile)\n    implementation(libs.jetbrains.annotations)\n    implementation(libs.jts.core)\n    implementation(libs.mapbox.vector.tile.java)\n    implementation(libs.protobuf.java)\n\n    compileOnly(libs.lombok)\n    annotationProcessor(libs.lombok)\n    testCompileOnly(libs.lombok)\n    testAnnotationProcessor(libs.lombok)\n\n    testImplementation(libs.jmh.core)\n    testImplementation(libs.jmh.generator.annprocess)\n    testImplementation(libs.junit.jupiter.api)\n    testImplementation(libs.junit.jupiter.params)\n    testImplementation(libs.sqlite.jdbc)\n    testRuntimeOnly(libs.junit.jupiter.engine)\n    testRuntimeOnly(libs.junit.jupiter.platform.launcher)\n}\n\ntest {\n    jvmArgs \"-Dcom.google.protobuf.use_unsafe_pre22_gencode\"\n    // Forward benchmark.iterations to the test JVM so tests can read it via Integer.getInteger()\n    systemProperty 'benchmark.iterations', System.getProperty('benchmark.iterations', '1')\n    useJUnitPlatform {\n        excludeTags 'benchmark'\n    }\n    testLogging {\n        outputs.upToDateWhen { false }\n        showStandardStreams = true\n        events \"passed\", \"skipped\", \"failed\"\n        exceptionFormat = \"full\"\n    }\n    finalizedBy jacocoTestReport\n}\n\njacocoTestReport {\n    reports {\n        xml.required = true\n        html.required = false\n    }\n    dependsOn test\n}\n\njava {\n    // Optional feature for GeoJSON support, which adds a dependency on JTS IO Common.\n    // This allows users to opt in to GeoJSON support without forcing the dependency on all users.\n    registerFeature('geojson') {\n        usingSourceSet(sourceSets.create('geojson'))\n        dependencies {\n            implementation(libs.jts.io.common)\n        }\n    }\n    toolchain {\n        languageVersion = JavaLanguageVersion.of(21)\n    }\n}\n\nspotless {\n    java {\n        importOrder()\n        target 'src/*/java/**/*.java'\n        googleJavaFormat()\n        removeUnusedImports()\n    }\n}\n\ntasks.register('compileWrapper', Exec) {\n    workingDir = project.projectDir\n    doFirst {\n        if (System.properties['os.name'].toLowerCase().contains('windows')) {\n            executable \"../resources/compile-windows.bat\"\n        } else {\n            executable \"./compile-wrapper.sh\"\n        }\n    }\n}\n\n// compileJava.dependsOn compileWrapper // Disabled due to CMake compatibility issues\n\ngradle.projectsEvaluated {\n    tasks.withType(JavaCompile).tap {\n        configureEach {\n            options.compilerArgs << \"-Xlint:unchecked\"\n            options.compilerArgs << \"-Xlint:deprecation\"\n            options.compilerArgs << \"-Xlint:all\"\n        }\n    }\n}\n\ntasks.withType(Test).configureEach {\n    useJUnitPlatform()\n}\n\ntasks.register('validateSemver') {\n    doLast {\n        def version = project.version\n        if (version == null || version == 'unspecified') {\n            throw new GradleException(\"Version is not set. Please provide a version using -Pversion=<version>\")\n        }\n\n        def semverPattern = /^\\d+\\.\\d+\\.\\d+$/\n\n        if (!version.matches(semverPattern)) {\n            throw new GradleException(\"Version '$version' is not a valid semantic version. Expected format: major.minor.patch (e.g., 1.0.0, 2.1.3)\")\n        }\n\n        println \"✓ Version '$version' is valid semver\"\n    }\n}\n\nmavenPublishing {\n    publishToMavenCentral(true)\n    signAllPublications()\n\n    coordinates(\"org.maplibre\", \"mlt\", version)\n\n    publishToMavenCentral.dependsOn validateSemver\n\n    pom {\n        name = \"MapLibre Tile Specification\"\n        description = \"Java implementation of the MapLibre Tile (MLT) specification\"\n        url = \"https://github.com/maplibre/maplibre-tile-spec\"\n\n        licenses {\n            license {\n                name = \"The Apache License, Version 2.0\"\n                url = \"https://www.apache.org/licenses/LICENSE-2.0.txt\"\n            }\n        }\n\n        developers {\n            developer {\n                id = \"maplibre\"\n                name = \"MapLibre contributors\"\n                url = \"https://github.com/maplibre\"\n            }\n        }\n\n        scm {\n            connection = \"scm:git:git://github.com/maplibre/maplibre-tile-spec.git\"\n            developerConnection = \"scm:git:ssh://github.com:maplibre/maplibre-tile-spec.git\"\n            url = \"https://github.com/maplibre/maplibre-tile-spec\"\n        }\n    }\n}\n\n// Disable signing for local publishing\ntasks.withType(Sign) {\n    onlyIf { !gradle.startParameter.taskNames.contains('publishMavenPublicationToMavenLocal') }\n}\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/BenchmarkUtils.java",
    "content": "package org.maplibre.mlt;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.data.MapboxVectorTile;\n\npublic class BenchmarkUtils {\n  private BenchmarkUtils() {}\n\n  public static void encodeTile(\n      int z,\n      int x,\n      int y,\n      Map<Integer, byte[]> encodedMvtTiles,\n      Map<Integer, ByteArrayInputStream> encodedMvtTiles2,\n      Map<Integer, byte[]> compressedMvtTiles,\n      Map<Integer, byte[]> encodedMltTiles,\n      String path,\n      String separator)\n      throws IOException {\n    final var encodedMvtTile = getMvtFile(z, x, y, path, separator);\n    encodedMvtTiles.put(z, encodedMvtTile.getLeft());\n    encodedMvtTiles2.put(z, new ByteArrayInputStream(encodedMvtTile.getLeft()));\n    compressedMvtTiles.put(z, EncodingUtils.gzip(encodedMvtTile.getLeft()));\n\n    final var columnMapping = new ColumnMapping(\"name\", \":\", true);\n    final var columnMappings = List.of(columnMapping);\n    final var columnMappingMap = ColumnMappingConfig.of(Pattern.compile(\".*\"), columnMappings);\n    final var isIdPresent = true;\n    final var metadata =\n        MltConverter.createTilesetMetadata(\n            encodedMvtTile.getRight(), columnMappingMap, isIdPresent);\n\n    final var allowIdRegeneration = true;\n    final var allowSorting = true;\n    final var optimization =\n        new FeatureTableOptimizations(allowSorting, allowIdRegeneration, columnMappings);\n    final var optimizations =\n        TestSettings.OPTIMIZED_MVT_LAYERS.stream()\n            .collect(Collectors.toMap(l -> l, l -> optimization));\n    final var config =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(true)\n            .useFSST(true)\n            .optimizations(optimizations)\n            .build();\n    final var encodedMltTile =\n        MltConverter.encode(encodedMvtTile.getRight(), metadata, config, null);\n    encodedMltTiles.put(z, encodedMltTile);\n  }\n\n  private static Pair<byte[], MapboxVectorTile> getMvtFile(\n      int z, int x, int y, String path, String separator) throws IOException {\n    var tileId = String.format(\"%s%s%s%s%s\", z, separator, x, separator, y);\n    var mvtFilePath = Paths.get(path, tileId + \".mvt\");\n    var encodedTile = Files.readAllBytes(mvtFilePath);\n    var decodedTile = MvtUtils.decodeMvt(mvtFilePath);\n    return Pair.of(encodedTile, decodedTile);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/OmtDecoderBenchmark.java",
    "content": "package org.maplibre.mlt;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.BenchmarkMode;\nimport org.openjdk.jmh.annotations.Fork;\nimport org.openjdk.jmh.annotations.Level;\nimport org.openjdk.jmh.annotations.Measurement;\nimport org.openjdk.jmh.annotations.Mode;\nimport org.openjdk.jmh.annotations.OutputTimeUnit;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.Setup;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.Threads;\nimport org.openjdk.jmh.annotations.Warmup;\nimport org.springmeyer.VectorTileLayer;\n\n/**\n * Benchmarks for the decoding performance of OpenMapTiles schema based tiles into the MVT and MLT\n * in-memory representations.\n */\n@State(Scope.Benchmark)\n@OutputTimeUnit(TimeUnit.MILLISECONDS)\n@BenchmarkMode(Mode.AverageTime)\n@Threads(value = 1)\n@Warmup(iterations = 5)\n@Measurement(iterations = 5)\n@Fork(value = 1)\npublic class OmtDecoderBenchmark {\n  /* java-vector-tile library */\n  private static final Map<Integer, byte[]> encodedMvtTiles = new HashMap<>();\n  /* mapbox-vector-tile-java library */\n  private static final Map<Integer, ByteArrayInputStream> encodedMvtTiles2 = new HashMap<>();\n  private static final Map<Integer, byte[]> compressedMVTiles = new HashMap<>();\n  private static final Map<Integer, byte[]> encodedMltTiles = new HashMap<>();\n  private static final String SEPARATOR = \"_\";\n\n  @Setup\n  public void setup() throws IOException {\n    encodeTile(2, 2, 2);\n    encodeTile(3, 4, 5);\n    encodeTile(4, 8, 10);\n    encodeTile(5, 16, 21);\n    encodeTile(6, 32, 41);\n    encodeTile(7, 66, 84);\n    encodeTile(8, 134, 171);\n    encodeTile(9, 265, 341);\n    encodeTile(10, 532, 682);\n    encodeTile(11, 1064, 1367);\n    encodeTile(12, 2132, 2734);\n    encodeTile(13, 4265, 5467);\n    encodeTile(14, 8298, 10748);\n  }\n\n  @Setup(Level.Invocation)\n  public void resetInputStreams() {\n    for (var is : encodedMvtTiles2.values()) {\n      is.reset();\n    }\n  }\n\n  private void encodeTile(int z, int x, int y) throws IOException {\n    BenchmarkUtils.encodeTile(\n        z,\n        x,\n        y,\n        encodedMvtTiles,\n        encodedMvtTiles2,\n        compressedMVTiles,\n        encodedMltTiles,\n        TestSettings.OMT_MVT_PATH,\n        SEPARATOR);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ2() throws IOException {\n    var mvTile = encodedMvtTiles.get(2);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ3() throws IOException {\n    var mvTile = encodedMvtTiles.get(3);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ4() throws IOException {\n    var mvTile = encodedMvtTiles.get(4);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ5() throws IOException {\n    var mvTile = encodedMvtTiles.get(5);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ6() throws IOException {\n    var mvTile = encodedMvtTiles.get(6);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ7() throws IOException {\n    var mvTile = encodedMvtTiles.get(7);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ8() throws IOException {\n    var mvTile = encodedMvtTiles.get(8);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ9() throws IOException {\n    var mvTile = encodedMvtTiles.get(9);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ10() throws IOException {\n    var mvTile = encodedMvtTiles.get(10);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ11() throws IOException {\n    var mvTile = encodedMvtTiles.get(11);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ12() throws IOException {\n    var mvTile = encodedMvtTiles.get(12);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ13() throws IOException {\n    var mvTile = encodedMvtTiles.get(13);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeMvtMapboxZ14() throws IOException {\n    var mvTile = encodedMvtTiles.get(14);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ2() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(2);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ3() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(3);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ4() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(4);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ5() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(5);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ6() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(6);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ7() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(7);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ8() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(8);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ9() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(9);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ10() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(10);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ11() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(11);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ12() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(12);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ13() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(13);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n\n  @Benchmark\n  public Map<String, VectorTileLayer> decodeCompressedMvtMapboxZ14() throws IOException {\n    var compressedMvTile = compressedMVTiles.get(14);\n    var mvTile = EncodingUtils.unzip(compressedMvTile);\n    return MvtUtils.decodeMvtMapbox(mvTile);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/converter/encodings/fsst/FsstBenchmark.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.util.Base64;\nimport java.util.concurrent.TimeUnit;\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.BenchmarkMode;\nimport org.openjdk.jmh.annotations.Fork;\nimport org.openjdk.jmh.annotations.Measurement;\nimport org.openjdk.jmh.annotations.Mode;\nimport org.openjdk.jmh.annotations.OutputTimeUnit;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.Threads;\nimport org.openjdk.jmh.annotations.Warmup;\n\n@State(Scope.Benchmark)\n@OutputTimeUnit(TimeUnit.SECONDS)\n@BenchmarkMode(Mode.Throughput)\n@Threads(value = 1)\n@Warmup(iterations = 5)\n@Measurement(iterations = 5)\n@Fork(value = 1)\npublic class FsstBenchmark {\n\n  private static final Fsst JAVA = new FsstJava();\n  private static final Fsst JNI = new FsstJni();\n\n  // ~30 bytes\n  private static final byte[] SMALL =\n      Base64.getDecoder().decode(\"SGV1YmFjaExpcHBlS2V0dGJhY2hTdGV2ZXI=\");\n  private static final SymbolTable SMALL_ENCODED = JAVA.encode(SMALL);\n  // ~600 bytes\n  private static final byte[] MEDIUM =\n      Base64.getDecoder()\n          .decode(\n              \"UsO8c3RlcmJlcmdFaXNlbmJlcmdLYXNzZWxlciBLdXBwZUJpbHN0ZWluVm9nZWxoZXJkTWlsc2VidXJnSG9oZSBHZWJhSGVsZHJhc3RlaW5PaG1iZXJnR3Jvw59lciBLbm9sbGVuU2Nod2VmZWxrb3BmR3Jvw59lciBCZWVyYmVyZ0FsdGVyIEJlcmcgKEhhaW5pY2gpUG9wcGVuYmVyZ0Jyb2NrZW5Sw7xja2Vyc2JpZWxHcm/Dn2VyIEthbG1iZXJnS8O8bnplbHNiZXJnSm9zZXBoc2jDtmhlV2VnZW5lcnNrb3BmUm/Dn2LDvGhsTcO2bmNoc2JlcmdXaW5ka25vbGxlbkVyYmJlcmdTdGVsbGJlcmdEb3JuYmVyZ0xvdGhyYWjDvGdlbEZlaHJlbmJlcmdBaGxzYnVyZ0hlaW5yaWNoc2jDtmhlV2V0dGVuYnVyZyAoSGFpbmxlaXRlKU3DvGhsZW5zdGVpblNjaG5lZWtvcGZBbGhlaW1lckdyb8OfZSBMYWl0ZVNvaXNiZXJnS2V1ZGVsc2t1cHBlRXNwaWdNYXVsa3VwcGVWaWt0b3JzaMO2aGVHcm/Dn2VyIEF1ZXJiZXJnSG9ja2VscmFpbkJvY2tHcm/Dn2VyIEV0dGVyc2JlcmdSaWVjaGhlaW1lciBCZXJnQnViZW5iYWRlciBTdGVpbldvbGZza29wZkJ1Y2hzY2hpcm1iZXJnU2F1aMO8Z2VsSGFuZ3N0ZWluSHV0c2JlcmdHZWllcnNow7xnZWxHZXJiZXJzdGVpblJvc3Nrb3BmV2lja2VuLUJlcmdCcmFha2JlcmdTY2htaWVkc3dpZXNlbmtvcGZCw7xobEdyb8OfZXIgRmluc3RlcmJlcmdHcm\");\n  private static final SymbolTable MEDIUM_ENCODED = JAVA.encode(MEDIUM);\n  // ~23kb\n  private static final byte[] LARGE =\n      Base64.getDecoder()\n          .decode(\n              \"THljw6llIGfDqW7DqXJhbCBldCB0ZWNobm9sb2dpcXVlIEhlbnJpIEJlcmdzb25Mb3VpcyBCbGFuY0dhcmUgZGUgbCdFc3RGb25kYXRpb24gb3BodGFsbW9sb2dpcXVlIEFkb2xwaGUgZGUgUm90aHNjaGlsZE1hcm9jQ2hhbXBpb25uZXRDZXNhcmlhIEV2b3JhIC0gUm9zYSBQYXJrc0NvbGzDqGdlIFNvbmlhIERlbGF1bmF5Q2VudHJlIG3DqWRpY2FsSMO0cGl0YWwgZGUgam91ciBMw6lvcG9sZCBCZWxsYW5JbnN0aXR1dCBMYXNlciBWaXNpb24gZGVzIEJ1dHRlcyBDaGF1bW9udENyaW3DqWVSb3NhIFBhcmtzTWFyY2FkZXQgLSBQb2lzc29ubmllcnNQbGFjZSBIw6liZXJ0SMO0cGl0YWwgZGUgam91ciBHZW9yZ2VzIFZhY29sYVBhcmMgQ2hhcGVsbGUtQ2hhcmJvblBvcnRlIGRlIGxhIENoYXBlbGxlT3B0aWNsYWlyQmFyYsOocyAtIFJvY2hlY2hvdWFydENow6J0ZWF1IFJvdWdlQ2jDonRlYXUgTGFuZG9uTWFyeCBEb3Jtb3lMZXMgQ2FyaWF0aWRlcyBkJ0FiYmV2aWxsZUjDtHBpdGFsIEZlcm5hbmQgV2lkYWxTdGFsaW5ncmFkQ2xpbmlxdWUgVsOpdMOpcmluYWlyZSBkdSBGYXVib3VyZ0xhIEJ1dHRlUmlxdWV0U2VjcsOpdGFuIC0gQnV0dGVzIENoYXVtb250VHJpc3RhbiBUemFyYUxlcyBSb3Nlc0N1c3RpbmUgLSBSYW1leVBham9sIC0gRMOpcGFydGVtZW50VsOpbGlnb0NyaW3DqWUgLSBDdXJpYWxNYXRlbGFzIEZhY3RvcnlDb2xsw6hnZSBIZW5yaSBCZXJnc29uR2FyZSBkdSBOb3JkIChNw6l0cm8pUm9zYSBQYXJrcyAtIEN1cmlhbEphdXLDqHPDiWNvbGUgcG9seXZhbGVudGUgZGVzIFBvaXNzb25uaWVyc0NvbG9uZWwgRmFiaWVuTG9uZyBDaGFuZ1BvbnQgTWFyY2FkZXRTaW1wbG9uTWFnZW50YSAtIE1hdWJldWdlIC0gR2FyZSBkdSBOb3JkR29sZCBHU01GbGFuZHJlQ2FuYWwgU2FpbnQtTWFydGluUG9pc3Nvbm5pw6hyZUdhcmUgZGUgbOKAmUVzdEV1Z8OobmUgU3VlUGFqb2wgLSBSaXF1ZXRQYXJ2aXMgUm9zYSBQYXJrc8OJdmFuZ2lsZSAtIEF1YmVydmlsbGllcnNBZnJpY2EgaW4gYSBCb3hTcXVhcmUgZHUgMjEgQXZyaWwgMTk0NEjDtHBpdGFsIExhcmlib2lzacOocmVCb2xpdmFyVHJvcGljb3NtZXRpcXVlTWF0aHVyaW4gTW9yZWF1IC0gU2ltb24gQm9saXZhckNoYXBlbGxlIC0gQ2FpbGxpw6lEZWxpY2UgS3lveWFNb25jZWF1IEZsZXVyc0xpYnJhaXJpZSBkZXMgT3JndWVzRG91ZGVhdXZpbGxlVmVyZHVuw4lnbGlzZSBOb3RyZS1EYW1lLWRlLWwnQXNzb21wdGlvbiBkZXMgQnV0dGVzLUNoYXVtb250QXRsYXNLaW5nIENvY2tQbGFjZSBkZSBUb3JjeVBham9sTWFnZW50YUFsYmVydCBLYWhuSmF1csOocyAtIFN0YWxpbmdyYWRQbGFjZSBGcmFueiBMaXN6dExhIEZheWV0dGUgLSBQb2lzc29ubmnDqHJlTGFiYXRBTUogQmF0aW1lbnRNYXJvYyAtIEZsYW5kcmVDb25kb3JjZXRJYmlzIE9ybmFub0NvY2NpTWFya2V0Q2FtYnJhaVnFjXNvUGhpbGlwcGUgZGUgR2lyYXJkT3JkZW5lciAtIE1hcnggRG9ybW95TWFnZW50YS1HYXJlIGRlIGwnRXN0Qm91Y3J5T3B0aWNhbFJ1ZSBkZSBNZWF1eExhIENoYXBlbGxlUm9jaGVjaG91YXJ0IC0gQ2xpZ25hbmNvdXJ0TGUgUGFpbiBkZSBsYSBMaWJlcnTDqUVzcGFjZSBTcG9ydGlmIFBhaWxsZXJvbkdhcmUgZHUgTm9yZCAoUkVSKU1hcmNow6kgU2VjcsOpdGFuRHIuIEdyYXZlc0NhcnJlZm91ciBDaXR5R3JhbmdlIGF1eCBCZWxsZXMgLSBKdWxpZXR0ZSBEb2R1U2FtYnJlIGV0IE1ldXNlTUlBTVBpc2NpbmUgUGFpbGxlcm9uQ3JpbcOpZSAtIEFyY2hlcmVhdUphZCBWb3lhZ2VzUGFyYWRpc1NreU5ldENlbnRyZSBJbnRlcnByb2Zlc3Npb25uZWwgZGUgRm9ybWF0aW9uIGRlcyBDb21tZXJjZXMgZGUgbCdBbGltZW50YXRpb25OYW5kdSBNaW5pIE1hcmtldFNxdWFyZSBNb250aG9sb25QYXJpcyBHYXJlIGR1IE5vcmTDiWNvbGUgbWF0ZXJuZWxsZSBkdSBEw6lwYXJ0ZW1lbnRTYWludC1CcnVub011bGxlckxlIENhZHJlIE5vaXJMYXZlcmllIE5ldCDDoCBTZWNSdWUgUmF5bW9uZCBRdWVuZWF1IChhc2NlbnNldXIpTGEgRmF5ZXR0ZSAtIER1bmtlcnF1ZU1hZ2VudGEgLSBNYXViZXVnZVJhZGlndWV0UGxhY2UgZHUgTWFyb2NMdW4nT3JBbmRyw6lDcsOoY2hlIENvbGxlY3RpdmVNYWRvdSBTYW5ndWluQ29uZG9yY2V0IC0gVHJ1ZGFpbmVQcm9ncmVzcyBTYW50w6lQbGFjZSBkZSBsYSBDaGFwZWxsZUdyb3VwZSBTY29sYWlyZVJlc3RhdXJhbnQgQmFsYWRpw4ljb2xlIE5vcm1hbGUgU29jaWFsZUTDqXBhcnRlbWVudCAtIE1hcnggRG9ybW95TWFyY2jDqSBTZWNyw6l0YW4gKDI2KVJvbmQtcG9pbnQgZGUgbGEgQ2hhcGVsbGVRdWFpIGRlIGxhIFNlaW5lIC0gU3RhbGluZ3JhZMOJY29sZSBNYXRlcm5lbGxlTS4gS2hhbiBNb2JpbGUgRmFzaGlvblRpbnRhbWFycmVWYWxlbmNpZW5uZXNaZW4mQ29Dcmltw6llIC0gQXViZXJ2aWxsaWVyc1BhcmlzIENvbGxlZ2Ugb2YgQXJ0Uml2b2xpIFJldG91Y2hlQ29sbMOoZ2UgR8OpcmFyZCBQaGlsaXBlQ29sbMOoZ2UgR2VvcmdlcyBDbGVtZW5jZWF1VUZSIGRlIE3DqWRlY2luZSBQYXJpcyBEaWRlcm90IC0gU2l0ZSBWaWxsZW1pbsOJY29sZSBtYXRlcm5lbGxlIGRlIFRvcmN5QXV4IETDqWxpY2VzIGQnQWJyYWhhbUN1cmlhbCAtIEFyY2hlcmVhdcOJY29sZSBtYXRlcm5lbGxlIFJpY2hvbW1lTGEgTWFpc29uIE1va3JhdFBhbGFpcyBkdSBXZW56aG91Q2Fyb2xlw4ljb2xlIFN1cMOpcmlldXJlIGRlIFZlbnRlIGV0IGQnRXhwb3J0YXRpb27DiWNvbGUgcHJpbWFpcmUgQ3Vnbm90Q3LDqGNoZSBCbGV1ZUNyaW3DqWUgLSBSb3NhIFBhcmtzQ29sbMOoZ2UgcHJpdsOpIFNhaW50LVZpbmNlbnRUZW5kYW5jZUNoZXogSGFtaWRMYSBDb2lmacOocmXDiWNvbGUgcHJpbWFpcmUgQ2hhbXBpb25uZXRDdXJpYWwgLSBDcmltw6llQ29sbMOoZ2UgUm9sYW5kIERvcmdlbMOoc0NGQSBJR1NMeWPDqWUgdGVjaG5vbG9naXF1ZSBKdWxlcyBTaWVnZnJpZWRMeWPDqWUgcHJvZmVzc2lvbm5lbCBFZG1vbmQgUm9zdGFuZMOJY29sZSDDqWzDqW1lbnRhaXJlIFBoaWxpcHBlIGRlIEdpcmFyZENhaWwgLSBEZW1hcnF1YXloYWx0ZSBuYXV0aXF1ZSBkZSBsYSBWaWxsZXR0ZUNhbmFsIDEzMU1haXNvbiBaaWx2ZWxpU3VubnnDiWNvbGUgw6lsw6ltZW50YWlyZSBwcml2w6llIFNhaW50ZS1NYXJpZVBhcnRlbmFpcmUgQ3LDqGNoZVByZXNzaW5nIFVuaS1QcmVzc0x5Y8OpZSBwcm9mZXNzaW9ubmVsIHByaXbDqSBNYXJjZWwgTGFteUNhbWHDr2V1Q3LDqGNoZSBjb2xsZWN0aXZlIERlbGVzc2VydENvbGzDqGdlIEFpbcOpIEPDqXNhaXJlw4ljb2xlIHByaW1haXJlIE1hdGhpc1N0ZWxsaWHDiWNvbGUgw6lsw6ltZW50YWlyZSBkZSBsYSBHdWFkZWxvdXBlR2FyZSBkdSBOb3JkTGEgY8WTdXIgZGVzIHBhaW5zQ29sbMOoZ2UgR2VvcmdlcyBNw6lsacOoc0NvbGzDqGdlIExhbWFydGluZURyYWdvbiBXb2tTcXVhcmUgU2ltcGxvbi1BbWlyYXV4w4ljb2xlIHByaW1haXJlIENsaW5nbmFuY291cnRTYWxsZSBkZSBtdXNjdWxhdGlvbiBQYWlsbGVyb27DiXRhYmxpc3NlbWVudCBzdXDDqXJpZXVyIHByaXbDqSBQcm9ncmVzcyBTYW50w6lTZWNvdXJzIHBvcHVsYWlyZSAtIEbDqWTDqXJhdGlvbiBkZSBQYXJpc0x5Y8OpZSBHw6luw6lyYWwgTGFtYXJ0aW5lw4ljb2xlIE1hdGVybmVsbGUgQm95LVplbGluc2tpw4ljb2xlIMOpbMOpbWVudGFpcmUgZGUgVG9yY3lNYWdlbnRhIC0gTWF1YmV1Z2Ug4oCTIEdhcmUgZHUgTm9yZEJvIEJ1blN1cCBjYXJlZXJMYSBQaWNjb2xhIFNpY2lsaWFJTlNFRUMgU2Nob29sIG9mIEJ1c2luZXNzICYgRWNvbm9taWNzQ0ZBIFN0ZXBoZW5zb27DiWNvbGUgUG9seXZhbGVudGUgUGFqb2xKYXJkaW4gZHUgMTIyIHJ1ZSBkZXMgUG9pc3Nvbm5pZXJzU0hSb2Nyb3kgU2FpbnQtVmluY2VudC1kZS1QYXVsUmVsYWlzIExhIFBvc3RlQ2Fpc3NlIG5hdGlvbmFsZSBkJ2Fzc3VyYW5jZSB2aWVpbGxlc3NlTGEgQ3Jpw6llUGF0aW5vaXJlIFBhaWxsZXJvbkNlbnRyZSBJbnRlciBFbnRyZXByaXNlIGRlIEZvcm1hdGlvbiBlbiBBbHRlcm5hbmNlw4ljb2xlIHBvbHl2YWxlbnRlIGRlIGxhIEdvdXR0ZSBkJ09ySmFyZGlucyBSb3NhIEx1eGVtYnVyZ0Nvb2snbiBXaXRoIENsYXNzIFBhcmlzw4ljb2xlIG1hdGVybmVsbGUgVGNoYcOva292c2tpTWF0aGlzQ29sbMOoZ2UgcHJpdsOpIEx1Y2llbiBkZSBIaXJzY2hNYWlzb24gZGVzIEFzc29jaWF0aW9uc1JvdG9uZGUgZGUgbGEgVmlsbGV0dGVTdXAgZGUgcHViQ3LDqGNoZSBjb2xsZWN0aXZlR3Jlc3NldMOJY29sZSBtYXRlcm5lbGxlIFNpbW9uIEJvbGl2YXJDb3VycyBGbG9yZW50w4ljb2xlIFNlY29uZCBEZWdyw6kgUHJvZi4gUHJpdsOpZSBTYWludC1Mb3VpcyBVbmlvbiBBY2Fkw6ltaWVWaWxsYSBkZSBDaGluZUx5Y8OpZSBwcm9mZXNzaW9ubmVsIEd1c3RhdmUgRmVycmnDqXBpenphTGVzIEphcmRpbnMgZCfDiW9sZcOJY29sZSDDqWzDqW1lbnRhaXJlIENhdsOpUGljYXJkQ29sbMOoZ2UgRGFuaWVsIE1heWVyw4ljb2xlIHBvbHl2YWxlbnRlIFNpbXBsb25HaWJlcnQgSm9zZXBoIC0gUGFyaXMgWFZJSUnDiWNvbGUgbWF0ZXJuZWxsZSBHb3V0dGUgZCdPck5KRGlhbW9uZCBIYWlyQ29sbMOoZ2UgQ2hhcmxlcyBQw6lndXlIYXV0LVBhcmxldXJzIFN5c3TDqG1lc8OJY29sZSBtYXRlcm5lbGxlIEFtaXJhdXhCVFMgSGVucmkgQmVyZ3NvbkNoYXVkcm9uIC0gU2FpbnQtTWFydGluTGUgY2FkcmUgbm9pcsOJY29sZSDDqWzDqW1lbnRhaXJlIE1hdXJpY2UgR2VuZXZvaXjDiWNvbGUgw6lsw6ltZW50YWlyZSBkJ09yYW5TcXVhcmUgRnJhbsOnb2lzZS1Iw6lsw6huZSBKb3VyZGHDiWNvbGUgbWF0ZXJuZWxsZSDDiWNsdXNlcyBTYWludC1NYXJ0aW5DcsOoY2hlIGNvbGxlY3RpdmUgbXVuaWNpcGFsZSBkZSBSb3VlbkxlIHBpZWQgw6AgdGVycmVIb2xpZGF5IElubiBFeHByZXNzIDogSMO0dGVsIFBhcmlzLUNhbmFsIERlIExhIFZpbGxldHRlTGEgRmF5ZXR0ZSAtIE1hZ2VudGFDYW1wdXMgbGFuZ3Vlc8OJY29sZSBldCBDb2xsw6hnZSBTYWludC1HZW9yZ2VzIChwcml2w6kpTHljw6llIHByb2Zlc3Npb25uZWwgU3V6YW5uZSBWYWxhZG9uSUNEIEludGVybmF0aW9uYWwgQnVzaW5lc3MgU2Nob29sw4ljb2xlIMOJbMOpbWVudGFpcmUgRG91ZGVhdXZpbGxlTGEgTWFybW90acOocmUgUMOpdHJlbGxlTHljw6llIGfDqW7DqXJhbCBldCB0ZWNobm9sb2dpcXVlIHByaXbDqSBDaGFybGVzIGRlIEZvdWNhdWxkUGxhbmV0IENoYW50aWVyUG9saWNlIE5hdGlvbmFsZcOJY29sZSDDqWzDqW1lbnRhaXJlIENsYXVkZSBWZWxsZWZhdXhDb2xsw6hnZSBMYSBHcmFuZ2UgYXV4IEJlbGxlc1NxdWFyZSBBcmlzdGlkZSBDYXZhaWxsw6ktQ29sbDEwOCBDYWbDqUxlIE1vdWxpbiBkZSBsYSBQYWxldHRlw4ljb2xlIHByaW1haXJlIGQnYXBwbGljYXRpb25CaWJsaW90aMOocXVlIFbDoWNsYXYgSGF2ZWxJbnN0aXR1dCBkZSBHZXN0aW9uIFNvY2lhbGUgLSDDiWNvbGUgZGVzIFJlc3NvdXJjZXMgSHVtYWluZXNMaWJyYWlyaWUgTm9yZGVzdMOJY29sZSDDqWzDqW1lbnRhaXJlIGRlIGwnw4l2YW5naWxlQ2VudHJlIEludGVyLUVudHJlcHJpc2VzIGRlIEZvcm1hdGlvbiBlbiBBbHRlcm5hbmNlw4ljb2xlIMOpbMOpbWVudGFpcmUgT3VyY3FMYSBEaW1lbnNpb24gRmFudGFzdGlxdWVMYSBSZW5haXNzYW5jZUNvdWRlcmPDiWNvbGUgw4lsw6ltZW50YWlyZSBTaW1vbiBCb2xpdmFyTm8gQ29tbWVudFBhcmlzIENow6J0ZWF1IFJvdWdlw4ljb2xlIG1hdGVybmVsbGUgU2FpbnQtTHVjw4ljb2xlIHByaW1haXJlIEHDiWNvbGUgTWF0ZXJuZWxsZSBBcmNoZXJlYXVHb29kIElkZWFzIGZvciBFdmVyeWRheSBMaWZlTGlicmFpcmllIEZsYW1iZXJnZUZyYW5wcml4U3F1YXJlIE1hcmMtU2VndWluQWxob3VkYcOJcGljZXJpZSBmbGV1cmllw4ljb2xlIMOpbMOpbWVudGFpcmUgRXVnw6huZSBWYXJsaW7DiWNvbGUgcHJpbWFpcmUgZCdhcHBsaWNhdGlvbiBCTHljw6llIHByb2Zlc3Npb25uZWwgQXJtYW5kIENhcnJlbMOJY29sZSBwcmltYWlyZcOJY29sZSDDqWzDqW1lbnRhaXJlIEN1cmlhbENvbGzDqGdlIE1hcmllIEN1cmllw4ljb2xlIG1hdGVybmVsbGUgTG91aXMgQmxhbmNEb3VibGUgQm9uaGV1csOJY29sZSBtYXRlcm5lbGxlIE1hcmNhZGV0w4ljb2xlIG1hdGVybmVsbGUgTWFyeCBEb3Jtb3nDiWNvbGUgw6lsw6ltZW50YWlyZSBkJ0F1YmVydmlsbGllcnNDZW50cmUgZOKAmUFuaW1hdGlvbiBMYSBHcmFuZ2UgYXV4IEJlbGxlc8OJY29sZSDDqWzDqW1lbnRhaXJlTGEgTG91dmVLVENTcXVhcmUgUmF5bW9uZC1RdWVuZWF1UGFyYWRpcyBkZSBTdXNoaVBhcmlzIExhIEdvdXR0ZS1kJ09yQW50ZW5uZSBKZXVuZXMgTW9udC1DZW5pc8OJY29sZSBwcml2w6llIFNhaW50LUxhdXJlbnRMYSBGYXlldHRlIC0gTWFnZW50YSAtIEdhcmUgZHUgTm9yZENvbnNlcnZhdG9pcmUgbXVuaWNpcGFsIGR1IFhJWGUgSmFjcXVlcyBJYmVydExlIENlbnRxdWF0cmUgLSAxMDRMYSBMaWJyZXJpYUNhcmR5w4ljb2xlIG1hdGVybmVsbGUgZXQgcHJpbWFpcmUgU2FpbnQtR2VvcmdlcyAocHJpdsOpKUxpYnJhaXJpZSBGaWR1Y2lhaXJlSGFsdGUtZ2FyZGVyaWVBbnRlbm5lIEpldW5lIEZsYW5kcmVzTcOpbcOpIGRhbnMgbGVzIE9ydGllc1NxdWFyZSBTaW1wbG9uTHljw6llIHByb2Zlc3Npb25uZWwgSGVjdG9yIEd1aW1hcmRHYXJlIGR1IE5vcmQgTm9jdGlsaWVuU3F1YXJlIFBhdWwgUm9iaW5TcXVhcmUgTWFkZWxlaW5lIFRyaWJvbGF0aUtpbmcgUGFyaXMgTW9iaWxlRm9vZCBIb3VzZUZyYW5rb2RlY2jDiWNvbGUgw6lsw6ltZW50YWlyZSBKZWFuLUZyYW7Dp29pcyBMw6lwaW5lUGl6emVyaWEgVG9kYSBMJ0FrZWxGcmFuc2kgU2hvcEJhbmFkaXIgQ2VudGVyIFBhcmlzQ29sbMOoZ2UgRWRtb25kIE1pY2hlbGV0QnVpIEJlYXV0w6nDiWNvbGUgcHJpbWFpcmUgSGVucmkgTm9ndcOocmVzTG9uZ3RlbXBzLi4uQ29pZmZ1cmUgU3R5bGVQYXJpcyBNb250aG9sb25DcsOoY2hlIGNvbGxlY3RpdmUgUGhpbGlwcGUgZGUgR2lyYXJkUHVibGlvdGjDqHF1ZUFsaXphIEFmcm9TcXVhcmUgZGUgQ2xpZ25hbmNvdXJ0U3F1YXJlIEV1Z8OobmUgVmFybGluSmFyZGluIFJhY2htYW5pbm92R29sZGVuIEZvb2TDiWNvbGUgw6lsw6ltZW50YWlyZSBSaWNob21tZUFsbG8gUmFwaWRvIFBpenphWXVwaGEgQmVhdXTDqSA5WmVicmEgU2hvZXNDaW1ldGnDqHJlIGRlcyBKdWlmcyBwb3J0dWdhaXNDcsOoY2hlIG11bmljaXBhbGUgQ2hhdW1vbnQgTGVwYWdlQ291cmlyQmlibGlvdGjDqHF1ZSBGcmFuw6dvaXMgVmlsbG9uU3F1YXJlIGRlIGxhIE1hZG9uZUxpYnJhaXJpZS1wcmVzc2VSYXdhc2kgdMOpbMOpY29tQ29sbMOoZ2UgVmFsbXlKYXJkaW4gQW1hZG91IEhhbXDDonTDqSBCw6LDiWNvbGUgbWF0ZXJuZWxsZSBQaWVycmUgQnVkaW7DiWNvbGUgw6lsw6ltZW50YWlyZSBBcXVlZHVjU3F1YXJlIEhlbnJpIENocmlzdGluw6lTaG9wSmFyZGluIEx1Yy1Ib2ZmbWFublNxdWFyZSBkZSBsJ8OJdmFuZ2lsZUNlbnRyZSBQYXJpcyBBbmltJyBDdXJpYWxQYXJpcyBCb2lub2RLaWxvdXRvdcOJY29sZSDDqWzDqW1lbnRhaXJlIEFybWFuZCBDYXJyZWxBIE9uZSBTaG9wTWF0b3PDiWNvbGUgcG9seXZhbGVudGUgw4ltaWxlIER1cGxvecOpR2FyZSBkdSBOb3JkIC0gRHVua2VycXVlTGUgUGFuYW1lQmFsYWRlcyBTb25vcmVzIFJlY29yZHPDiWNvbGUgw6lsw6ltZW50YWlyZSBQaWVycmUgQnVkaW5TcXVhcmUgZHUgUXVhaSBkZSBsYSBTZWluZVNxdWFyZSBDdXJpYWxMaWJyYWlyaWUgZHUgQ2FuYWxMYSBCYW5kZSBkZXMgQ2luw6lzTHljw6llIGfDqW7DqXJhbCBDb2xiZXJ0QWZyb2NsYXNzVGhpc2hhbiBTdXBlciBNYXJjaMOpRmx1eCBkJ2VuY3JlLiBDb21PJ1RhY29zUmFkaW8gTHlyZWxNb25vcCdMYSBSdWNoZSAtIMOJY29sZSBwcml2w6llIGRlcyBtw6l0aWVycyBkZSBsYSBjcsOpYXRpb25QYXBldGVyaWUgUGVyamFjVmVydCBkJ09KYXJkaW4gZGUgbCfDrmxvdCBSaXF1ZXRIdWJpesOJY29sZSDDqWzDqW1lbnRhaXJlIExvdWlzIEJsYW5jw4ljb2xlIFByaXbDqWUgU2FpbnQtQmVybmFyZCBkZSBsYSBDaGFwZWxsZUdsb2J1cyBTdGFyQWZybyBSdWJ5IENvc23DqXRpcXVlc1N1bmJ1cnlNYXJ0ZWwgQnJpY29sYWdlUGFyaXMgR2FyZSBkZSBsJ0VzdEJpYmxpb3Row6hxdWUgTWF1cmljZSBHZW5ldm9peFBob3RvIE9saXZlU2Fkw6lzaG9lc0dhcmUgZHUgTm9yZCAoZ2FyZSByb3V0acOocmUpR2FiIExpbmVMZSBSaWRlYXUgUm91Z2VPJ01vS2HDiWNvbGUgZHUgU29sZWlsQ29sbMOoZ2UgTWFyeCBEb3Jtb3lTcXVhcmUgTWFyY2VsIE1vdWxvdWRqacOJY29sZSDDqWzDqW1lbnRhaXJlIExhZmF5ZXR0ZVBhcmlzIE1ldWJsZXPDiXRvaWxl4oCLIExhZmF5ZXR0ZU9mZmljZSBEZXBvdFRTIFRlbGVjb21TQVJMIExlIERha2Fyb2lzR3JvdW5kIFplcm8sIE5hdGlvbmFsZSA3IFJlY29yZHNPcHRpYydBbGwgQXZlbnVlUG9saWNlIGRlcyBUcmFuc3BvcnRzQi5ELiBpbmZvcm1hdGlxdWVWSSBab25lRW1tYcO8c0NhbGkgTmFpbHNBdSBHcmVuaWVyIGR1IENoYW1wQ2FycmVmb3VyIEJpb0JpYmxpb3Row6hxdWUgQ2xhdWRlIEzDqXZpLVN0cmF1c3NMZSBCYXIgQ29tbXVuTGEgRmF5ZXR0ZSAtIFNhaW50LVF1ZW50aW4gLSBHYXJlIGR1IE5vcmRTcXVhcmUgU2FpbnQtQmVybmFyZCAtIFNhw69kIEJvdXppcmlMeWPDqWUgZ8OpbsOpcmFsIGV0IHRlY2hub2xvZ2lxdWUgamFjcXVhcmTDiWNvbGUgbWF0ZXJuZWxsZSBBcXVlZHVjw4ljb2xlIMOJbMOpbWVudGFpcmUgUHJpdsOpZSBMdWNpZW4gZGUgSGlyc2NoTsOpZ2F0aWYgKyBTZXJ2aWNlIGFyZ2VudGlxdWVBbWlyYWwgU3RvcmVSZWxheUNvdXJzIGRlcyBoYWxsZXPDiWNvbGUgbWF0ZXJuZWxsZSBldCDDqWzDqW1lbnRhaXJlIExvdWlzLUJsYW5jTU0gQWxpbWVudGF0aW9uTFNUQXV4IFJlbmRlei1Wb3VzIGRlcyBBbWlzTWFkZWVoYSAtIEJlYXV0w6kgZCdBZnJpcXVlS2lvc3F1ZSBkZSBQYXJpc0xlIE1lcmxlIE1vcXVldXJTcXVhcmUgTMOpb27DiWNvbGUgSW50ZXJuYXRpb25hbGUgQWxnw6lyaWVubmVTUksgRXhvdGlxdWVBIG9uZSBwcmludGVyc01haXNvbiBkJ0Vubm91ck1haXNvbiBkZSBsYSB2aWUgYXNzb2NpYXRpdmUgZXQgY2l0b3llbm5lIGR1IDEwZSBhcnJvbmRpc3NlbWVudFJNIGFsaW1lbnRhdGlvbiBnw6luw6lyYWxlRXhvIFN5bXBhTGUgTG9jYWxTdXBlcm1hcmNow6kgQXNpZSAtIEV4b0xpbGlhbmUgZGUgTWF0b3NCb2lzIERvcm1veUFDIG1hdGnDqHJlQmFyLVRhYmFjIExlIEJlcmdlcmFjRWRlbiBwaG9uZURpc2NvdW50IE1vYmlsZVNxdWFyZSBBbGFpbiBCYXNodW5nTHljw6llIEFsYmVydCBKYWNxdWFyZFNlcnZpY2UgZCdJbnZlc3RpZ2F0aW9uIFRyYW5zdmVyc2FsZVVuaXZlcnMgTGluZVl2ZXMgUm9jaGVyIFBhcmlzIEZsYW5kcmVUaGF2ZSBFeG9CLXZyYWNPcHRpYyBFeHBlcnTDiWNvbGUgw6lsw6ltZW50YWlyZSBDaGFicm9sQ29uc2VpbCBkZSBwcnVkJ2hvbW1lcyBkZSBQYXJpc1BvaW50IFNBdGVsaWVyIEZsb3JhbEdhbWJheE1haXNvbiBEb2R1IERpbmRvblBhcmlzIE9yZ3VlcyBkZSBGbGFuZHJlU04gU3ViYXRlbEVzcGFjZSBDaMOidGVhdSBMYW5kb25MaWJyYWlyaWUgQmF6YXLDiWNvbGUgRWxlbWVudGFpcmUgU2ltb24gQm9saXZhclRyYW5zZmx1eENhcnLDqSBWb3lhZ2VNYXJpb25uYXVkQW1hemluZyBCZWF1dHlDcsOoY2hlTWFtYSBDYWtlRmxldXJzIGRlIEZyYW5jZU9yaWVudGFsIERpc2NvdW50TGUgUGV0aXQgTWFyY2jDqUZhY2UgYSBGYWNlRnJhbmNlIENvbW9yZXMgVm95YWdlc8OJY29sZSBtYXRlcm5lbGxlIENhcnJlbEUuIExlY2xlcmMgUmVsYWlzQkFUVCBDT09QRWxlY3RybyBBZmZhaXJlc01hcmNow6kgRnJhbnByaXhFY29kYWlyIFBhcmlzQmlibGlvdGjDqHF1ZSBIZXJnw6lUaWVuZGEgRXNxdWlwdWxhc0lHQiBUw6lsw6ljb21FbW1hw7xzIGJvdXRpcXVlIHNvbGlkYWlyZUJsYWNrQm94ZUNvbGzDqGdlIEJlcm5hcmQgUGFsaXNzeVN1cGVyIE1hcmtldExlIEJsYW5jb0xhIFLDqWd1bGnDqHJlTGUgS2lvc3F1ZSBkZSBQYXJpc01hw69kYSBCZWF1dHlMJ8OJcGljZXJpZSBkdSAxMDREYW55IENhc2jDiWNvbGUgbWF0ZXJuZWxsZSBMw6lvbiBTY2h3YXJ0emVuYmVyZ1JlcHJvZ3JhcGhpZSBMZXMgSW1hZ2VzIE51bcOpcmlxdWVzw4ljb2xlIG1hdGVybmVsbGUgU2FkaSBMZWNvaW50ZUxpc3NhYyBPcHRpY2llbkF0ZWxpZXIgU29saUN5Y2xlUHJpbmNlIEpvcmdlR29sZE1pY3JvIGNyw6hjaGUgUGlyYXRlcyBkJ2VhdSBkb3VjZUwnSW52aXQnIMOgLWxpcmVTdXBlcmV0dGVMaWJyYWlyaWUgSXNsYW1pcXVlTHljw6llIHBvbHl2YWxlbnQgbCdJbml0aWF0aXZlTGlicmFpcmllIE11c2ljYWxlU2VydmljZXMgJiBUaWNrZXRzIFJBVFAgKEJhcmLDqHMgLSBSb2NoZWNob3VhcnQpR3VpY2hldCBUcmFuc2lsaWVuUnVlIGRlcyBQb2lzc29ubmllcnNDRiBpbmZvcm1hdGlxdWVDYXJyZWZvdXIgQ2l0eSBQYXJpcyBSaXF1ZXTDiWNvbGUgw4lsw6ltZW50YWlyZSBCZWx6dW5jZVBhcGV0ZXJpZSBNZXJjZXJpZSBKb3VybmF1eFBhcmlzIEluZm9ybWF0aXF1ZUJpam91IEpvY2VseW5lQmlvY29vcCBDYW5hbCBCaW9Fc3BhY2UgSmV1bmVzIEdvdXR0ZSBEJ09yTW9uIGNvbmNlcHQgaGFiaXRhdGlvbkxlcyBNYWxpY2lldXggZGUgTGFmYXlldHRlQ29sY2hpZGVGcmFuY2UgNEcgVMOpbMOpY29tS2lvc3F1ZSBKZXVuZXMgLSBMYSBHb3V0dGUgZCdPckhhdmFzIFZveWFnZXNBbWVyaWNhbiBDb3Ntw6l0aXF1ZXNNYXJjaMOpIENyaW3DqWUtQ3VyaWFsVmluZ3Rla2hvbmdMaXRvdGUgZW4gVMOqdGVDaXTDqSBTY29sYWlyZSBIZW5yaSBCZXJnc29uIC0gSmFjcXVhcmRBbGltZW50YXRpb24gR8OpbsOpcmFsZUNhcnJlZm91ciBNYXJrZXQgUGFyaXMgT3JuYW5vWmFyYVNPUyBNb2JpbGUgUG9pbnRMaWJyYWlyaWVPcmNoaWTDqWUgVGhhaSBCZWF1dMOpQ3VsdHVyZSBldCBMaWJlcnTDqU1haXNvbiBkZXMgQXNzb2NpYXRpb25zIGR1IFhJWGVGcnVpdHMgZXQgTMOpZ3VtZXNNYXJjaMOpIE9ybmFub0dhbGVyaWVzIGR1IE1vYmlsaWVyU29sZWlsTGVzIFjDqXJvZ3JhcGhlcyDDqWRpdGlvbnNEZXMgZmxldXJzIGNoZXogc29pTGUgQmlzdHJvdCBkdSBOb3JkTGUgQm9uIENvaW5MZSBMdXTDqXRpYUNvbGzDqGdlIEJvc3N1ZXQgLSBOb3RyZS1EYW1lQ29tbWlzc2FyaWF0IENlbnRyYWwgZGUgUG9saWNlIGR1IDEwZU1vbm9wcml4RG91YmxlIEV4ZW1wbGFpcmVJbnRlcm1hcmNow6kgRXhwcmVzc0FmcmkgUGhvbmVTcXVhcmUgQWxiYW4gU2F0cmFnbmVTcXVhcmUgZGUgSmVzc2FpbnRTdXDDqXJldHRlIGRlIEJvdWNyeUJvdXRpcXVlIGZvciBUb21vcnJvd0NoZXogRm91Y2hlciBNw6hyZSAmIEZpbGxlQWRvbmlzIEZsZXVyc0xpZGxQYXJpcyBQaGlsaXBwZSBkZSBHaXJhcmRTdXBlcmV0dGUgTWFya2V0Q29tbWlzc2FyaWF0IGRlIGxhIEdvdXR0ZSBkJ09yU3RvcmUgTUsyTWFyaW4gZCdFYXUgRG91Y2VEaWdpdGFsIEluZm9ybWF0aXF1ZUxlYWRlciBQcmljZUJhemFyIEwnT2xpdmVGcsOocmVzLVBob25lcy5uZXRKYXJkaW4gaW50w6lyaWV1ciBTYWludC1MYXphcmVQYXJpcyBMb3VpcyBCbGFuY01hcnJha2VjaFNxdWFyZSBMb3Vpc2UgZGUgTWFyaWxsYWNEw6lwaWwgU29mdE9wdGlxdWUgVG9yY3lMaWJyYWlyaWUgVmlnbmV0dGVzTWFpc29uIEFncmViaVBhcGV0ZXJpZXMgU2lsbEdsb2JhbCBQTSBFeHByZXNzRiZBIFRlbGVjb21KYWhhbiBtaW5pIG1hcmtldFVOSSBzdXJnZWzDqUxlIGZvdXJuaWwgZCdBbmRyw6l6aWV1eEx5Y2Ftb2JpbGVQYXJpcyBNYXJ4IERvcm1veUxpYnJlIFNlcnZpY2UgZGUgbGEgR2FyZVNBUkwgU3JpIEV4b3RpcXVlSmFyZGluIGRlIGxhIENvdXIgZGUgbGEgRmVybWUgU2FpbnQtTGF6YXJlU1lLIEFmcm9Qw6lww6kgU2FudGFuYUxlcyBOb3V2ZWF1eCBSb2JpbnNvbkJvZHkgTWludXRlTGVzIEJ1dmV1cnMgZCdFbmNyZU15IEF1Y2hhbkJpYmxpb3Row6hxdWUgR291dHRlIGTigJlPckJDSU0gaW5mb3JtYXRpcXVlSmFyZGluIE1hcmllbGxlLUZyYW5jb2lDZWxsU2FpbnQtTWFjbG91QXV0cmUgVnVlUGFyaXMgSmF1csOoc0ltcHJpbWVyaWUgUG9seXByaW1GbmFjTWNEb25hbGQnc0xhIFTDqnRlIGRhbnMgbGUgRnJvbWFnZVJhYXNpIFRlbGVjb21QYXJpcyBUcmF2ZWxTb2Npw6l0w6kgR8OpbsOpcmFsZUNyb2l4LVJvdWdlTGFtYXp1bmFTaGF5YW4gQ3LDqnBlUm9zZSBSb3VnZSBSZWxheGF0aW9uT3B0aWMgMjAwME5vY8OpYUFsbMO0IFNhcmluYUxlYmFyYUxhdGlubyBQaXp6YU3DqWRpYXRow6hxdWUgRnJhbsOnb2lzZS1TYWdhbktpc3NvIFN1biBDYXNoIGFuZCBDYXJyeUVzcGFjZSBGbGV1cnNIdW16YSBCZWF1dMOpUml2b2xpIFRlbGVjb21CaWpvdXRlcmllIFNhaW50LU1hcnRpbk5hdHVyYWxpYSBNYXJ4IERvcm1veUJpam91dGVyaWUgSG9ybG9nZXJpZVBvaXNzb25uaWVycyAtIE5vcmRBbmV4b051bWJlciBPbmUgLSBQcm9kdWl0cyBBZnJpY2FpbnNBdGVsaWVyIExlamV1c25lS2lmYWtQcmVzdGlzc2ltb1NlYmJhaCBwcmltZXVyc1RydWRhaW5lIE9wdGlxdWVNYWdhc2luIGRlIGNoYXVzc3VyZXNQcmluY3kgQmVhdXR5IFBhbGFjZUZyZW5jaCBDcsOqcGVzU2VjcsOpdGFuIE3DqW5hZ2VyTW9oYW4gSmV3ZWxsZXJ5IE1hcnRDb3B5LVRvcFBvaXNzb25uaWVycyAtIENoYW1waW9ubmV0VmVybmF6b2JyZXMgR3JlY29QZXJjaW5nIFN0YWxpbmdyYWRQYXJpcyBTdGFyIEZvb2RBcmMgZW4gQ2llbEd1ZXJyaXNvbEtveWFrYSBNYXJrZXRLYXZlcnkgQ2FzaCAmIENhcnJ5RMOpcMO0dCBkZSBMYSBDaGFwZWxsZVlhdG9vIFBhcnRvb0xlcyDDqXRoaXF1ZXTDqXNTdXBlciBtYXJrZXRFeG8gTWFya2V0QWRhbmFQYXJpcyBTY29vdGVyaXNPcHRpY0NNIEV4b3RpcXVlQ2hpY2tlbiBTcG90Q2FjdHVzICYgTWFpc29uLUNhZG9FdmEgVHJhdmVsc0FsaW1lbnRhdGlvbiBnw6luw6lyYWxlU0FSTCBTYW1pYU1hemFsYXkgQ291dHVyZUxlcyBkw6lsaWNlcyBkZSBsYSBDaGFwZWxsZU9wdGknbWlzdGVFbHNhICYgSnVzdGluU1RSIEFsaW1lbnRhdGlvbiBHw6luw6lyYWxlS2FuZ2Z1IE1hc3NhZ2VSb3lhbCBLZWJhYkxhIEJhbHVzdHJhZGVPcHRpcXVlTGEgUHJvc3DDqXJpdMOpQ2hhdXNzZSBDb25mb3J0TGEgcGF1c2UgdHJhaXRldXJEdWMgQ2hyaXN0b3BoZUxlIENpZWwgZXN0IMOgIHRvdXQgbGUgTW9uZGVDYXByaWNlIFN0eWxlUm9uZC1Qb2ludCBkZSBsYSBDaGFwZWxsZVNlbGVjdG91clNleHkgU2hvcFNoYXRoYSBUaW1lRm9vZExhIEdyYXBwZSBkJ09yQXV4IFF1YXRyZSBTYWlzb25zTGVzIEFsaXplcyBPcHRpY2llbnNBbHRvJ1RhY29zQ2hleiBUb255R2lmaVZpamF5U3Vid2F5TGUgcGV0aXQgbWFyY2jDqSBkZSBSaXF1ZXRNYXJjaMOpIGRlIExhIENoYXBlbGxlUGFyaXMgQm91dGlxdWUgR2FyZSBkZSBsJ0VzdEF1IHBhcyBkZSBTYWludC1Mb3Vpc0NoZXogR2FzdG9uS2luZ01GIDIwMDBTQVJMIENpc3NlICYgRmlsc1JEQyBCaW8gTmEgQmlvTGUgQsOpbmluUGFuZGEgRmFzdCBGb29kUGhvbmUgQm91dGlxdWVDcmVwJ1dheVBhcmlzIENhc2ggYW5kIENhcnJ5TGEgUGxhdGVmb3JtZSBkdSBCw6J0aW1lbnQgY29tcGFjdEQ0IENvc23DqXRpcXVlQm9keScgTWludXRlUnVlIE9yZGVuZXJab25lIE1vYmlsZUJpbyBHw6luw6lyYXRpb25MJ2FwcGFydGVtZW50IEVtbWHDvHNIRkMzOCBnb3VybWV0U28nRmFzdFBoYXJtYWNpZSBkZSBsYSBDaGFwZWxsZVByb3h5SW5mb3JtYXRpcXVlIE1vYmlsZVNhYmFiYVdheCBKb2xpIEFmcmlxdWVHb29kYXlBZGVzIG1vdG9zVGVsZS1Qb3AtTXVzaWtHdWluw6llIFN0b3JlTGUgUMOpbGljYW5QYXJpcyBTdG9yZUxlIEZsYXNoQnJpY29sZXhDYWlzc2UgZCfDiXBhcmduZSBQYXJpcyBMYSBDaGFwZWxsZURpZGltIEtlYmFiUGFyaXMgQ2FuYXDDqXNDcmVwZW1lbidCb25PbGl2ZXMsIMOpcGljZXMsIGZydWl0cyBzZWNzLCBldGMuLi5KREMgQWZyaWNhIC0gTGUgR3JlbmllciBkJ0FmcmlxdWVSb3NhIEluc3RpdHV0TGEgVmllIENsYWlyZUFlbGlhIER1dHkgRnJlZUxlIFByb3ZlbmNpYWxBQkkgRXhvdGlxdWVOaW5hIE1ldWJsZXMgJiBEZWNvw4lsw6lnYW5jZSAmIFZvdXNSaXF1ZXQgRmFzaGlvbkxlIFN1cGVyY29pbk1pc3Npw6BCZWF1dMOpcyBkdSBNb25kZUJhZ2VsIENvcm5lckF1IFLDqWd1bGF0ZXVyS2hhbiBUZWxlY29tbXVuaWNhdGlvbk1vbiBQZXRpdCBJbnN0aXR1dEVzdCdhaXIgVm95YWdlc1N1biBNb29uIFRlbGVjb21Nb24nRHdpY2gnQWZnaGFuIE1hcmtldEhvJ3Mga2ZlTGEgTWVkaW5hTMOpb25waG90RGhha2EgSW5mb3JtYXRpcXVlUm9jayB0aGUgS2lsaW1TYWJiYWggT3JpZW50YWxlZm9yIE1hYyAmIFBDUGFkbWEgdGVsZWNvbUxhbmRvbiBEw6lsaWNlc0xlIENvc3RhIFJpY2FTYXZldXIgMTlQb2ludCBTb2xlaWxTb3VuZGFyIFRyYXZlbHNMYSBSb3NlIGRlIFBhcmlzRyZNIENoYXVzc3VyZXNPdWVkLVJoaW91QnJpY29yb3V4WcO8biBQcmltZXVyIMOpcGljZXJpZSBmaW5lWGluIEthaSBZdWVWRUdBIFNrYXRlc2hvcEV1cm9wIFBob25lUGl0YSBCdXNpbmVzcyBDZW50ZXJGcm91LUZyb3UgTWVyY2VyaWUgQ29udGVtcG9yYWluZcOJcGljZXJpZSBkdSBDb2luIC0gQWxpbWVudGF0aW9uIEfDqW7DqXJhbGVJbGFuaW8gTW9iaWxlQkggc2Nvb3RlckxlIEZvbnRlbm95TCdBZHJpYXRpcXVlUGFqb2wgLSBQbGFjZSBIZXJiZXJ0UG9tbScgcmVzdG9DZW50cmUgRGVuaSBIZXNrb01vZHN1cHJlbUJvYm90byBCb25kZWtvIEtpbWlhRWFyLVdlbGwgTGFiTCdBcnQgZGUgVm9pck5hb3VyaSBDaXR5UGFya2luZyBaZW5wYXJrIE1hcnggRG9ybW95VHLDqW1hTGVzIElubm9jZW50c0xhIFZpZWlsbGUgUGllQ2hhdXNzZWVzIEhhdXQgZGUgR2FtbWVTdW4gVHJhdmVsTG9naW4gSW5mb3JtYXRpcXVlUmVzdGF1cmFudCBCb2RydW1FZGVzc2FEb3JhIFBob25lIFNob3BTdGFsaW5ncmFkIFBpZXJjaW5nQ2VudHJlIHNwb3J0aWYgTWljaGVsaW5lIE9zdGVybWV5ZXJDaG9oYW4gVGVsZWNvbUdsb2JhbCBHU01QYXJhZGlzIGQnQWZyaXF1ZVbDqXTDqXJpbmFpcmUgQ3JpbcOpZUp1aWN5ICYgVGFzdHlLaWxvZ3JhbW1lTGUgVGl2YW91YW5lVHphcmFTIFMgQkQgQ2xhc3NpYyBLaGFkaWphICYgQnJvc0VycmFuY2VzRWwgTm9wYWxMZSBSYWxseWVBZ2VuY2UgZGUgdm95YWdlcyBIYW5uYSBUb3Vyc1NpbW9u4oCZcyBib3V0aXF1ZSBob3RlbETDqWNvcmFzb2xMYXZlcmllQWxwaGEgQmV0YSBQcmVzc2VMYSBjYXZlIMOgIGNpZ2FyZXNTRlJDcmltw6llIC0gT3VyY3FEZSBMJ0F1dHJlIEPDtHTDqSBkZSBsYSBCdXR0ZVNtYXJ0Q2FybGEgQmVhdXTDqUxhIFRyaW5jYW50ZUZvb2RpUmV0cm9tb3Rpb25KdW1ib1NtYXJ0IE11bHRpbWVkaWFNYW0nQXlva2FTb2Npw6l0w6kgQ2FwcmljZSBDb21lc3RpcXVlUmF5YW5HYWxlcmllIEJvaG8gQm9ow6htZUUuTGVjbGVyYyByZWxhaXNZYW1haGFNYWdhc2luIGQnQWNjZXNzb2lyZXMgcG91ciBUw6lsw6lwaG9uZXNNYW1hIEFmcmljYUZldWlsbGUgw6AgRmV1aWxsZUxhIENvcm5lIGRlIGzigJlBZmZyaXF1ZUF5bmdhcmFuIENlbnRlckwnQWJzdXJkZSBJbXBvc3R1cmVNaWMgTWFjTWVkaWNhIFZpc2lvblBIIE9wdGljQydlc3QgdG91dCB2dURhaWx5IFNob3BwaW5nIENhc2ggJiBDYXJyeUJlYXV0eSBRdWVlbjfDqG1lIGLigJlhcnRLZW56aWVMZSBSZXBhaXJlTmljb2xhc0dhbGVyaWUgV2VuZ2VQb2l2cm9uIFJvdWdlQWTDqXF1YXRCaWpvdXRlcmllIE9yIEZsYW5kcmVCcmFnYXJkRmxhbmRyZSBBbGltZW50YXRpb25BdSByZW5kZXotdm91cyBkZXMgY2hhdWZmZXVyc0NoZXogUGHDr0xlIFNpbXBsb25NYWdhc2luIGRlIFdheEphcmRpbiBhdXggNCBTYWlzb25zQWxpbWVudGF0aW9uIE1lZGl0ZXJyYWluZUwnQWZmYW3DqVNDUyBJbmZvcm1hdGlxdWVNYXJpb24gR3JhdVNBUkwgRGplbmVib3UgLSBBbGltZW50YXRpb24gR8OpbsOpcmFsZVRlYW0gUFNHIFN0cmVldCBBcnRCb3V5Z3VlcyBUZWxlY29tRXVybyBRdWluY2FpbGxlcmllT3JkZW5lciAtIFBvaXNzb25uaWVyc0NlbnRyZSBzcG9ydGlmIFRyaXN0YW4gVHphcmFMJ09jY2l0YW5lQmVhdXTDqSBEaXZpbmVQcm9tZXRvdXJNYWNoaWNhZG91VkdHYXJhZ2UgQXJjaGVyZWF1Uy5TLiBDYXNoIGFuZCBDYXJyeUjDtHRlbCBNZXJyeWxNb2luY2hlcnBhcmZ1bUxlIE1hcmNow6kgRXhvdGlxdWVMZSBKYXJkaW4gZGUgRGplbmFPcHRpJyBDbGFpcmVBbGFpbiBBZmZsZWxvdUxlcyBDZXJjbGVzIGRlIGxhIEZvcm1lQml6eidhcnRFdG9pbGUgVmVydGVCaWpvdXRlcmllICsgT3JTdGFyIEFmcm8gTG9va1RpbiBUaW5BbGltZW50YXRpb24gR8OpbsOpcmFsZSBQcm9kdWl0cyBBZnJpY2FpbnNCaW8gQycgQm9uWW9uZ0NhZsOpIENhY2jDqUplYW4tQ2xhdWRlIEJpZ3VpbmVTdGFydC1VcCBWb3lhZ2VzUGFyaXMgUHJpbWV1cnMgQ3LDqG1lcmllSXR6aWtBdSBGaWwgZHUgVmluUGFyaXNsYW5kSmFycmEgQ291dHVyZUwnRXNjYWxlTGUgQm9saXZhckNoZW5uYWlDxZN1ciBkZSBOYXR1cmVDaGV6IENsYXJhT3JhbmdlUGl6emEgRmxhdm9yTGUgTG9mdE1pY2hlbGFuZ2Vsb0TDqWNvdXZyaXIgUGFyaXNCQSBQYXJpc09wdGlxdWUgU2VjcmV0YW5QaGFybWFjaWUgZGUgbCdPdXJjcUZhbW91cyBQYXJpc0tpbmdzIFRyYXZlbHNMYSBIdWNoZSBOb3JtYW5kZUxlIER1bmhpbGxDb21wdG9pciBkdSBCYW5nYWxFeGlzTGF2ZXJpZSBNIEdTYXJsIE1hYXZheUlzdGFuYnVsIE9yaWVudGFsRzIwUGhhcm1hY2llIEV1cm9ww6llbm5lQ2hleiBMYWhvdXNzaW5lTGVzIGTDqWxpY2VzIGRlIFJvc2EgUGFya3NQaGFybWFjaWUgZHUgU3F1YXJlTmV3IFRoYWkgU2FuT2tyb3MgVHVuZGVKIFdlbGxWb3lhZ2VzIExhZmF5ZXR0ZUFsbG8gUGl6emFTdXDDqXJldHRlIEFmcmljYWluZVBvaXNzb24gZHUgTW9uZGVRdWF0cmUtVmluZ3QgRGV1eFTDqWzDqSBNw6luYWdlciBQYXJpc2llblRhbmdlciAtIFBsYWNlIGR1IE1hcm9jR2FzdG9uIFRlc3NpZXIgLSBSb3NhIFBhcmtzIFJFUlBoYXJtYWNpZSA3NUNvbmZvcnR1bWFCZWUnIFNob2Vzw4lsb2RpZUV1cm8gQmFuZ2xhIE11bHRpIFNlcnZpY2VzTGUgU2FpbnQtQmVub2l0RGVwYXJ0ZW1lbnQgLSBQYWpvbFBhcmFkaXNlIFBpenphTXlocmEgVHJvcGljYWxDaXR5IFZpc2lvbkxhIFBhbnRow6hyZSBPc2XDiXBpY2VyaWVBYmR1bGxhIEtleWFhbi5jb20gVGF4aXBob25lTGVzIGNvdWxldXJzIGQnw45sZSBkZSBGcmFuY2VOZWVsdW1TdGFyIENvbU1hbWFnYXlhSMO0dGVsIEliaXMgU3R5bGVzIFBhcmlzIENyaW3DqWUgTGEgVmlsbGV0dGVMYSBSb3NlcmFpZUNlbnRyZSBkZSBraW7DqXNpdGjDqXJhcGllIFJpcXVldEJyaWNrdG9wIFBpenphU2VudGV1ciBkZXMgVmllc05hdHVyYWxpYUxlIEphc21pbiBDYWbDqUxlIFBldGl0IENhcmlsbG9uRmFicmlxdWUgRGFpZ3JlbW9udEF1YmVyZ2UgZHUgQm9uaGV1ckNvdXJzIGRlcyBIYWxsZXNEw6lqw6AgVnVlUGF0aXN0b3JpQ2F0aHkgT25nbGVzQm91Y2hlcmllIE1lbmd1ZWxsZXRKYXJkaW5zIGQnRW9sZUthcnRoaWthIEludGVybmF0aW9uYWxLb2JhbCBDYXNoIGFuZCBDYXJyeUNow6J0ZWF1IE1hcnRpYWxDb3JuZWlsIDM2Qm91bGFuZ2VyaWUgRGUgQ3JpbcOpZU5hb21pZSBCZWF1dMOpWXZlcyBSb2NoZXJSZXN0YXVyYW50IExvY29tb3RpdmVMZSBSYXRhcG9pbCBkdSBGYXVib3VyZ1RhbmcgRnLDqHJlc0xhIENhc3Nlcm9sZUxSQiBNZWRpYXRlbGVjb21MdXhvcHRpY0xlIENow6J0ZWF1IExhbmRvblNoYWFlQ2hpY2tlbiBLaW5nQnJhc3NlcmllIEwnT2xpdmVOb3RhTGVzIEFuaW1hbHNEaWFieS1LYWJhUGhvbmUgMjAwMEF1IGRlbGEgZHUgUENDaGV6IEZvdWNoZXIgTcOocmUgZXQgRmlsbGUyMjVDYXNpbm9HdW1ibyBZYXlhSGFpciBGYXNoaW9uIFN0eWxlQmFucXVlIFBvcHVsYWlyZSBSaXZlcyBkZSBQYXJpc0Nhc2EgUm9tYU5haWxzIE1pbnV0ZUpvaG4gUGF1bEVhc3QgQnVua2VyTWFyY2jDqSBHb3VybyBkJ0FiaWRqYW7DgCAyIFBhc1Njb290ZXIgQ2VudGVyIFBhcmlzSMO0dGVsIGRlIEJlbGxldnVlIFBhcmlzw4lnbGlzZSBTYWludC1TYXZhQ0hJQ0ggS0VCQUJMJ2VzY2FsZSBwb3VyIGxhIFBhaXhMZSBEamVubmUgSUlCcmlnaHQgMTAgT3B0aXF1ZVNreSBQaG9uZVBoYXJtYWNpZSBDb2hlbiBTb2xhbENhcnJlZm91ciBNYXJrZXRBbGwgSW4gUGhvbmVDb2NjaW5lbGxlT3B0aWNhbCBTZXJ2aWNlIE8rQmVhdXR5IEdpcmxzSGFub3VtYW5QaGFybWFjaWUgVHVyZ290UC5ULlMuIFNBUkxSdWUgZGUgRmxhbmRyZSAoY2VudHJlIGNvbW1lcmNpYWwpS0lLTyBNaWxhbm9DZW50cmUgZGUgc2FudMOpIFBhcmlzIDE4w6htZUxlIFJvYmluZXQgZCdPckZveWVyIGQnw6l0dWRpYW50cyAtIENoZW1pbiBOZXVmTWFyaWUgRnJhbmNlIFByZXQgYSBwb3J0ZXJDR0VEIC0gY29tcHRvaXIgZCfDqWxlY3RyaWNpdMOpIFZhbG15R2FyYWdlIFBham9sQ2x1YiBSQVRQRWwgTXVuZG9BYmkgQmVhdXR5IFBhbGFjZUF1eCBTcG9ydHNXaWxsaWFtJ3MgT3BlcmFMZSBHcmFuZCBQYXJxdWV0UGV1Z2VvdCBQU0EgUmV0YWlsIFBhcmlzIEdhcmUgZGUgbCdFc3RBbnRhbHlhSmFyZGluIMOpcGjDqW3DqHJlIEJhdWRlbGlxdWVNYWdhc2luIGRlIFRpc3N1c1NlcGhvcmFTb3VsIEZvb2RLYWxpZW50ZURhcm91c2FsYW0gQ291dHVyZUjDtHRlbCBSaXF1ZXRIw7R0ZWwgUGFyaXMgVmlsbGV0dGVQw6hyZSAmIEZpc2hDaGV6IEFtbWFkUGhhcm1hY2llIExhYnJlbHlDYXNpbm8gU2hvcFBoYXJtYWNpZSBCYW5hTWFyY2hhbmRlcyBkZSBjb3VsZXVyc0ZyZXNoICYgRmFzdEdyaWxsIElzdGFuYnVsTGUgTmF2aWdhdGV1ck15IEJydXNoQm9iJ3MgQmFrZSBTaG9wQm91dGlxdWUgUkFUUExhIFJhbWVNb25vcOKAmURhaWx5QWlkYSBCZWF1dMOpw4lwaWNlcmllIGRlcyBFbnZpcm9uc0dhcmUgZGUgbCdFc3QgLSBWZXJkdW5TbmFjayAxMTZHcmFuZCBHYXJhZ2UgQ2xpZ25hbmNvdXJ0TGEgVmllIGVzdCBCZWxsZUJWIENvbmdvIEV4b3RpcXVlUGlzY2luZSBkZXMgQW1pcmF1eER5bmFtb0wnYXZlbnR1cmUgw6AgVsOpbG9TYWxvbiBDaGFudGFsTWV1YmxlcyBQYXNjYWxUYWJhY0ZseW5hc0Jpc3Ryb3QgUGFwaWxsb25MYSBCYWd1ZXR0ZUTDqWxpY2VzIGRlIEZsYW5kcmVGcmFuY2UgQmFuZ2xhTGEgQ2FudGluYUxlIE1hdGhpc1BhcmlzIE5vcmQgR1NNTydSb3lhbENhaXNzZSBkJ8OJcGFyZ25lIFBhcmlzIE9yZGVuZXIgTWFpcmllQnJpY28gTWV1Ymxlc08nVnJhYyBkJ0Ftw6lsaWVDb3B5IFByZXNzw4lnbGlzZSBTYWludC1EZW55cyBkZSBsYSBDaGFwZWxsZU1hbGFMYSBNYWlzb24gQmxldWVUYXNza2FHYXJlIGRlIGwnRXN0IC0gQ2jDonRlYXUtTGFuZG9uSGFkaSBDb2lmZnVyZUxlIE5hcnZhbFNBUkwgTWFkaWJhQXV0b3VyIGR1IEZvdXJuaWxNdWx0aW1lZGlhIFNlcnZpY2VzSMO0dGVsIFRvcmN5TWFra2FsIEthZGFpUmVzaWRob21lIFBhcmlzIFJvc2EgUGFya3NIw7R0ZWwgZHUgVGVycmFnZVBvaW50IFBMZSBOZW1yb2RBa2Rlbml6UGl6emEgVGltZVZpZ25lc0xlIFBldGl0IEpvc2VwaEJyYXNzZXJpZSBMZSBDb3JhaWxKYW5ldCAtIENvbGxlY3Rpb25UcmlvIEFmcmljYUFydExhYlBpenphTlNQIHNhcmxDb3V0dXJlIE1vZGUgZXQgQ3JlYXRpb25QJkwgVHJlc3Nlc1F1ZWxsaSBkaSBsYVBpc2NpbmUgSMOpYmVydFRhc3Nha2FTaXJhc2EgRW50cmVwcmlzZUdyYW5kIHN1ZENJQ09wdGljJyBhbGwgQXZlbnVlU3BhIE5pbmdqaW5nIFNhbG9uIGRlIG1hc3NhZ2VQcmVzc2luZyBMeXNtdXNEb21pbm8ncyBQaXp6YVN1ZXogQmF6YXJTdHJlZXQgQmFuZ2tvayBMb2NhbCBGb29kUmVzdGF1cmFudCBpbmRpZW4gJiBDcsOqcGVyaWVNZW1vQ290IENvdExhIFRlcnJhc3NlUsOpc2lkZW5jZSBSb215IFNjaG5laWRlckF1ZGlrYUF1IE3DqWRpdGVycmFuw6llbkhvcml6b24gVW5pQmFzaWxpcXVlIFNhaW50ZS1KZWFubmUtZOKAmUFyY01vdWphaGlyIFRyYXZlbHNMZSBwYXZpbGxvbiBhdXggZmxldXJzTGUgVmljcSBkJ0F6aXJMZXMgRMOpbGljZXMgZGVzIEZhdWJvdXJnc1BhcmZ1bWVyaWUgQnVyZGluVGFjb1NoYWtlTWFyeCBleG90aXF1ZVp1YmVpZGEgQ29zbcOpdGlxdWVzTGUgQ29tbWVyY2VQb2xhciBNYXRlcmlhdXhDYWbDqSBDYWNhaHXDqHRlw4lnbGlzZSBTYWludC1MdWNFdXJvcGUgS2ViYXBNYW5kZWVxTGVzIEZvbmR1cyBkZSBsYSBSYWNsZXR0ZVBoYXJtYWNpZSBkdSBTbXBsb25CdXJnZXIgS2luZ1BhcmtpbmcgUGFyaXMgMTlLaW1zb3VCZWxsZXZpbGxlUmFvdWwgRm9sbGVyZWF1TGV6J0FydHNQbGFjZSBkdSBDb2xvbmVsIEZhYmllblByaW50IEV4cHJlc3NQaGFybWFjaWUgSG9tw6lvcGF0aXF1ZSBNYXViZXVnZUxlIFBhY3RvbGVCZWF1dMOpIFVuaXF1ZUZvcnVtIGQnQWRqYW3DqVBob25lRXh0YXNpYU55c2FIw7R0ZWwgQmVsZm9ydFBob25lIEhvdXNlQ2hleiBMYWxsYUNvcGllIExhIENoYXBlbGxlQWdlbmNlIE15cmlhbSB2b3lhZ2VzU2FtIEtlYmFiQ2FyaWVsTGUgVmVsbGVmYXV4TGEgQmVsbGUgTWV1bmnDqHJlR3JhbWVlbiBCZW5nbGFMJ8OJY29sZSBCdWlzc29ubmnDqHJlTGUgQ2hhbnNvbm5pZXLDiWdsaXNlIE5vdHJlLURhbWUgZHUgQm9uIENvbnNlaWxEaWFtIEludGVybmF0aW9uYWwgLSBNZXJjZXJpZSBHw6luw6lyYWxlWWFsb3VuYSBCb3V0aXF1ZUxhIFJvdG9uZGUgSMOpYmVydGluZVJlc3RhdXJhbnQgUGFjaGFBdSBCb250ZW1wc0FmcmlrYSBCb2JvdG9MZXMgRm91cm5pbHMgZGUgRnJhbmNlUy5QLiBSYWp1V2FzaCduIGRyeUxlIFBoZW5peCBkJ09yUGhhcm1hY2llIENvbG9uZWwgRmFiaWVuTGUgVmVycmUgVGFxdWluRXF1aXZhbGVuemFCaWpvdXRlcmllIE5vYWhNb2RlIFBsdXNDYWbDqSBkZSBsYSBHYXJlUGFya2luZyBaZW5wYXJrIFBvcnRlIGTigJlBdWJlcnZpbGxpZXJzWWF0YWxpZVJveWFsIFJvY2hlIENob3VhcnRIUyBNdWx0aSBTZXJ2aWNlc1Byw6lmw6lyZW5jZSBDb3Ntw6l0aXF1ZVNxdWFyZSBNYXVyaWNlLUtyaWVnZWwtVmFscmltb250Qm91bGFuZ2VyaWUgVmVydGVGYWl6YSBCZWF1dHlMJ8OJY2xhdCBkZXMgTWFpbnNQaGFybWFjaWUgU291c3Nhbi1BaXplbm1hbkxlIENoYWxhbmRFeG90aXF1ZSBMS04gQWZyaWNhQW5na29yUGhhcm1hY2llIGR1IE1hcmNow6lMYSBHYXJlbnRpdGEtVHdvU0FSTCBBaXIgU3VuTWlzcyBMaXF1ZU9wdGljaWVuIE5lc3NBc2lhIFBhY2lmaWMgVHJhZGVIw7R0ZWwgTGUgQmllbnZlbnVlT2hhbGV5IFlhYWtvdkZseSBDb21tdW5pY2F0aW9uQm91Y2hlcmllIFN1cGVybWFya2V0IEhhw690acOJdG9pbGUgZCdBZnJpcXVlIFZveWFnZXNSdWUgZGUgQ2hhYnJvbE15cmlhbSBDb2lmZnVyZU1heXNhcmEgSW5zdGl0dXRUZW5uaXMgZGUgdGFibGVCb3VjaGVyaWUgR291cmF5YUphcmRpbiBwYXJ0YWfDqSBkdSBUcsOoZmxlIGQnw4lvbGVEw6lsaWNlIFBhaW5MYSBQcmluY2lwZXNzZUVsZWN0cmFBc2lhIE1hcmtldEF1IEJvbmhldXIgZGVzIG1hcmnDqXNPcHRpY2FsIERpc2NvdW50QWxwaGEgQnJpY28gU2VydmljZXNMYSBNYXJtaXRlTGUgQ2Fmw6kgUHJ1ZCdoT3V0bGV0IENyaW3DqWVTdXNoaSBDcmltw6llU2Fsb24gZGUgbWFzc2FnZSB0aGFpbGFuZGFpcyBKaWEgSmlhUnVlIExhIEZheWV0dGVIb3Jsb2dlckxlIFJpZGVhdSBkZSBGZXJGdWxhbm9IZWxsYSBNYXJpYWdlR8OpbsOpcmFsZSBkJ09wdGlxdWVDYW5hbCBTdXNoaUxhcyBDaGljYXNGYXVib3VyZyBQb2lzc29ubmnDqHJlSMO0dGVsIFZpY3EgZCdBemlyTCdBbWJhc3NhZGVHU00gV2FsbGV0QmFsYXZpbnlhZ2FyTENMQm91Y2hlcmllIEFsIEFtYW5hTGEgSGFsbGVQQyBEaWFnbm9zdGljTGEgTWFpc29uIFRoYcOvTWVnYXdheFRhdGlFY29sb2dpZSAyMDAwQ2hvdSBDb20gVG91dFRoYcOvIDE4TUFBIFRlbGVjb21MJ1VuaXZlcnMgZGUgTMOpb1RvdGFsw4lwaWNlcmllIEFmcmljYWluZUxlcyBGcmFuZ2luc0xhdmVyaWUgU0JTQXUgVGFxdWV0VHJpYW5nbGUgZCdvclRow6nDonRyZSBQaXhlbEFsZXhpc0dvcGFsIGFuZCBDbyBTdXBlcm1hcmNow6lEb3BwaW9KdSBZb3VCYXJyYWN1ZGFMYSBDb3Vwb2xlS2hhbHNhIFRleHRpbGVFY2xhdCBCZWF1dMOpUGl6emEgTmlub0FnZW5jZSBUcmFuc2lsaWVuQ2hleiBDeXJpbGxlRm9udGFpbmUgZGUgQmVsbGV2aWxsZU0uQ09NTWFya3MgYW5kIFNwZW5jZXIgRm9vZEF1IFJlbmRlei12b3VzIGR1IE1hcmlhZ2VMZSBUcmljeWNsZUZhbGllRm9udGFpbmUgV2FsbGFjZVNyaSBNYWhhbFJlc3RhdXJhbnQgTWFuZGFyaW4gZHUgTWFyY2jDqUxlIE11cmdlckjDtHRlbCBDcmltw6llSmlhbHknc1BhcmFkaXNlIFRyYXZlbHNPJ1BhcmNMZSBDb2xiZXJ0Q2VudHJlIEhwIDI1IFJldm9sdXRpb25TQVJMIERlYm9SYWptYWhhbEjDtHRlbCBkdSBQcsOpUHJlc3NpbmdsJ1VuaXZlcnMgZGUgTMOpb0jDtHRlbCBkZXMgT2x5bXBpYWRlc0JhYyDDoCBzYWJsZVDDonRpc3NlcmllIGRlcyBTdWx0YW5zS2lkcyBNYWpvck9wdGlxdWUgR3VlekTDqWNvcmF0aW9uIG1hdMOpcmlhdXggZGUgRmxhbmRyZUFmcm8tc29pbnNDcsOpZGl0IGR1IE5vcmRib3VpLWJvdWlHZW5lcmF0b3IgSG9zdGVsc0FiZG91bG1hamlkTW9ub3AnRGFpbHlTbydGb29kc1ZhbGVnZUJvdXRpcXVlIEdyYW5kZXMgTGlnbmVzaWJpcyBQYXJpcyBDYW5hbCBTYWludCBNYXJ0aW5FdmFuZ2lsZSAtIEF1YmVydmlsbGllcnNMYSBQZXJnb2xhIGQnSXRhbGlhSmFyZGluIHBhcnRhZ8OpIGRlIGxhIGJ1dHRlIEJlcmdleXJlRUxNIEluZm9ybWF0aXF1ZUFtYmFhbFN1bnNoaW5lTGEgUGFpbGxhc3NlRmllc3RhIMOoIGJhc3RhUMOpZGljdXJlRW1tYcO8cyBEw6lmaVIuQy5BIE11bHRpIFJ2aWNlc0dhbyBsaSBzYWxvbiBkZSBtYXNzYWdlTCdFdGVybmVlbFRoZSBOZXcgTG9vayBGYXNoaW9uVW4gQW1vdXIgZGUgZmxldXJEcmFmdCBBdGVsaWVyc1LDqXNpZGVuY2UgZHUgUHLDqVBhbmRhIE1hcmlhZ2VMZXMgUHJpbWV1cnMgZHUgQ2jDonRlYXVCbGFuY2hpc3NlcmllIGNvdXR1cmUgU2VjcsOpdGFuU2FiYmFoU2hhbWl0aGFOZXNzRmFzYWwgQ29pZmZ1cmVNYWlzb24gQ2\");\n  private static final SymbolTable LARGE_ENCODED = JAVA.encode(LARGE);\n  // 230kb\n  private static final byte[] XLARGE;\n  private static final SymbolTable XLARGE_ENCODED;\n\n  static {\n    XLARGE = new byte[LARGE.length * 10];\n    for (int i = 0; i < 10; i++) {\n      System.arraycopy(LARGE, 0, XLARGE, i * LARGE.length, LARGE.length);\n    }\n    XLARGE_ENCODED = JAVA.encode(XLARGE);\n  }\n\n  @Benchmark\n  public SymbolTable encodeSmallJava() {\n    return JAVA.encode(SMALL);\n  }\n\n  @Benchmark\n  public SymbolTable encodeMediumJava() {\n    return JAVA.encode(MEDIUM);\n  }\n\n  @Benchmark\n  public SymbolTable encodeLargeJava() {\n    return JAVA.encode(LARGE);\n  }\n\n  @Benchmark\n  public SymbolTable encodeExtraLargeJava() {\n    return JAVA.encode(XLARGE);\n  }\n\n  @Benchmark\n  public SymbolTable encodeSmallJni() {\n    return JNI.encode(SMALL);\n  }\n\n  @Benchmark\n  public SymbolTable encodeMediumJni() {\n    return JNI.encode(MEDIUM);\n  }\n\n  @Benchmark\n  public SymbolTable encodeLargeJni() {\n    return JNI.encode(LARGE);\n  }\n\n  @Benchmark\n  public SymbolTable encodeExtraLargeJni() {\n    return JNI.encode(XLARGE);\n  }\n\n  @Benchmark\n  public byte[] decodeSmallJava() {\n    return JAVA.decode(SMALL_ENCODED);\n  }\n\n  @Benchmark\n  public byte[] decodeMediumJava() {\n    return JAVA.decode(MEDIUM_ENCODED);\n  }\n\n  @Benchmark\n  public byte[] decodeLargeJava() {\n    return JAVA.decode(LARGE_ENCODED);\n  }\n\n  @Benchmark\n  public byte[] decodeExtraLargeJava() {\n    return JAVA.decode(XLARGE_ENCODED);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/data/rle_PartOffsets.csv",
    "content": "2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;2;3;3;3;3;3;2;2;3;2;2;2;3;2;2;3;2;3;3;2;2;3;3;3;3;3;2;2;2;2;3;3;3;2;2;2;3;2;3;3;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;3;2;2;3;2;2;2;3;3;2;2;3;2;2;3;2;3;2;3;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;3;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;3;3;2;2;3;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;3;2;3;2;2;2;2;2;2;3;3;3;2;3;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;3;3;3;3;3;2;3;3;2;2;3;2;2;2;3;2;2;3;3;3;3;2;2;3;2;2;3;3;3;3;3;3;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;3;2;2;3;2;3;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;3;3;3;3;3;3;3;3;3;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;3;3;2;2;2;2;2;3;3;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;3;3;3;2;3;2;3;2;2;2;3;2;2;2;2;2;3;3;2;3;2;2;3;2;2;2;3;2;2;2;2;2;2;2;3;3;2;2;3;3;2;3;2;2;2;2;2;2;2;3;2;2;3;3;3;3;2;3;3;2;3;2;3;3;2;2;3;2;2;2;2;3;3;3;2;2;2;2;3;2;2;3;2;2;3;3;2;2;3;2;2;2;3;2;3;2;3;2;2;3;3;3;3;3;3;2;3;3;2;2;3;3;2;2;3;2;2;2;2;2;3;3;2;2;2;3;2;2;2;3;3;2;2;2;2;3;3;2;3;2;2;3;3;3;2;2;2;2;2;2;3;3;3;3;3;3;2;2;2;3;3;3;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;3;2;3;2;2;3;3;2;2;3;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;3;3;2;3;2;2;2;2;2;2;3;3;2;3;4;2;2;3;3;3;3;3;2;2;2;2;2;3;2;2;2;3;2;2;3;3;3;2;2;2;2;3;2;2;3;3;3;3;3;3;2;2;2;2;2;2;3;2;2;3;2;3;2;3;2;2;2;2;2;2;3;2;2;2;2;2;3;3;3;2;2;3;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;3;3;2;2;3;3;3;3;2;2;3;2;2;3;2;2;2;3;2;2;3;2;3;3;3;2;2;2;2;3;3;2;3;3;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;3;3;3;3;2;2;2;2;2;2;3;3;3;3;3;3;3;2;3;3;2;3;2;2;2;2;2;2;3;2;2;3;3;2;3;3;3;3;3;2;2;2;2;3;3;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;3;2;2;3;2;2;3;3;2;2;3;3;2;3;2;2;2;2;2;2;2;3;3;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;3;2;2;2;2;3;2;2;2;2;2;3;2;3;2;3;3;2;3;2;2;3;3;3;3;2;2;2;2;3;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;3;2;2;2;2;2;2;2;2;3;2;2;3;3;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;3;2;3;2;3;2;2;3;2;2;2;3;2;2;2;3;3;2;2;3;2;2;2;2;2;3;2;2;2;2;3;3;2;3;3;2;2;3;2;2;2;2;3;2;2;3;3;3;3;4;3;3;2;2;2;2;2;2;2;3;2;2;3;3;3;2;2;3;2;2;3;3;2;2;3;2;2;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;3;3;2;2;3;2;2;3;3;2;2;3;2;3;2;3;2;2;2;2;2;2;3;2;2;2;2;3;3;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;3;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;3;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;6;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;3;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;5;2;3;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;4;2;3;2;2;2;3;2;2;2;2;2;3;2;3;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;6;2;2;2;2;2;2;3;2;4;2;2;2;4;2;4;3;3;2;2;3;2;2;2;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;4;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;4;2;3;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;4;2;4;3;2;2;2;2;2;2;2;2;2;2;6;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;4;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;4;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;5;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;4;2;2;4;2;4;3;5;2;3;4;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;4;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;3;2;3;3;2;2;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;7;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;4;2;2;3;2;3;2;2;2;2;4;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;4;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;5;4;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;4;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;3;2;2;2;2;2;2;3;2;2;2;3;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;3;3;2;3;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;4;2;3;2;2;2;2;4;2;2;2;2;4;2;2;2;3;2;2;2;2;2;3;2;3;2;2;2;2;3;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;2;2;2;3;2;2;4;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;3;3;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;5;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;6;2;2;2;5;4;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;7;5;3;4;2;2;3;3;4;3;5;3;3;3;6;3;10;2;3;3;2;3;3;3;2;3;3;2;2;7;6;4;4;2;2;3;2;5;2;3;2;4;4;4;2;2;2;2;2;3;15;3;3;7;6;3;4;9;7;5;3;2;3;3;5;3;5;3;3;5;7;4;2;2;3;3;3;3;3;3;2;5;5;7;3;3;2;5;2;2;2;3;2;3;3;3;3;6;4;4;2;2;3;3;2;5;5;4;15;5;2;2;3;5;4;2;3;2;2;3;3;2;2;2;2;2;2;2;3;7;5;4;9;6;3;5;3;3;3;3;3;3;3;6;3;10;3;5;3;4;3;3;7;3;5;3;3;3;5;3;2;2;3;3;3;2;6;7;3;3;3;2;7;3;5;5;3;3;13;5;5;3;3;4;3;3;3;3;2;5;3;3;5;2;2;2;2;4;3;3;5;3;3;4;2;5;4;3;3;3;3;3;5;3;2;4;3;3;3;8;3;3;2;2;3;5;3;2;5;3;3;3;5;5;3;3;3;3;3;3;7;2;2;5;2;2;2;5;3;3;3;5;3;5;7;3;3;3;6;3;3;3;4;2;2;3;3;4;5;9;3;7;5;3;4;3;3;8;6;2;3;6;5;2;3;5;3;7;9;3;3;3;5;2;3;5;10;3;3;3;2;2;3;3;4;3;2;2;3;3;3;2;2;3;3;2;2;2;2;3;2;2;5;2;2;3;4;3;5;5;3;3;3;3;2;2;2;2;3;3;3;4;3;3;2;3;9;6;2;5;7;2;6;3;7;5;3;2;3;2;2;4;4;2;2;3;3;3;5;3;2;3;2;4;3;3;2;5;2;2;3;5;3;3;6;2;2;5;2;2;3;3;3;5;2;3;2;2;3;3;3;3;4;3;3;3;3;7;2;2;5;2;2;3;3;7;3;3;3;3;2;2;3;3;4;2;2;3;2;4;3;3;2;3;2;4;6;3;2;2;2;2;2;2;3;2;6;3;3;3;3;3;7;3;3;5;3;3;3;3;3;3;3;2;2;2;3;2;3;4;3;3;3;2;2;2;3;2;2;2;3;5;3;3;3;4;4;4;2;6;3;3;3;3;3;3;3;3;3;2;2;2;3;2;3;7;3;2;2;2;2;3;6;3;3;4;3;3;3;2;2;3;3;3;2;3;6;3;3;3;3;3;2;2;3;3;2;2;2;2;3;3;2;2;3;3;3;3;2;2;5;3;2;2;3;3;2;2;2;3;6;3;3;3;3;3;4;3;5;3;2;2;3;3;5;6;3;3;2;5;6;5;5;5;3;3;5;3;2;2;4;10;5;4;4;4;3;4;3;3;3;7;3;3;2;4;3;3;3;3;6;7;4;8;3;4;3;11;3;3;3;3;2;2;3;3;2;2;2;3;3;3;3;3;3;2;2;4;4;3;3;5;9;3;15;3;3;3;2;2;3;3;2;2;3;2;2;4;2;3;3;3;3;3;2;2;6;5;2;2;2;2;4;3;3;7;3;8;3;2;3;3;3;3;3;3;3;3;3;3;3;5;3;2;2;3;3;3;3;2;2;3;3;3;2;3;3;3;3;3;2;2;7;4;3;5;2;13;2;6;5;11;5;3;3;2;2;8;3;9;3;3;3;5;3;2;2;2;2;2;3;3;3;2;3;5;4;3;8;3;5;5;3;2;2;4;3;3;3;3;4;3;3;3;2;3;3;3;3;2;3;3;3;5;3;3;5;3;3;3;9;4;2;2;2;2;3;7;3;2;10;7;9;3;3;3;3;3;3;3;3;3;3;3;3;3;3;2;2;3;3;4;3;3;2;3;3;4;3;3;2;3;3;3;3;3;3;2;3;3;3;2;2;3;3;3;2;2;2;2;3;3;3;2;3;2;2;2;2;3;5;7;3;3;7;2;3;3;3;2;3;3;3;5;4;4;4;2;2;3;3;3;2;3;2;3;2;2;2;3;3;3;6;3;3;3;3;3;3;3;3;3;2;2;3;4;3;3;3;4;3;3;11;4;3;2;2;5;7;3;10;3;10;3;3;2;3;4;2;2;3;2;2;3;4;3;3;3;3;3;5;3;3;5;2;2;3;8;2;2;2;3;3;4;3;3;3;5;3;3;4;2;2;3;7;8;3;3;3;3;3;3;2;2;4;3;5;2;2;3;3;2;3;3;4;3;2;5;3;3;3;4;6;3;3;5;3;3;3;4;3;3;3;3;2;5;2;2;3;3;3;3;2;3;3;5;2;3;3;3;3;3;3;3;3;10;3;4;5;3;3;7;4;3;2;3;3;6;9;3;3;3;3;4;3;4;3;3;3;3;3;5;3;2;3;8;3;3;6;3;4;5;3;2;4;5;3;2;2;2;3;2;5;3;3;3;5;7;3;3;3;5;5;3;2;3;3;4;3;3;3;7;5;4;9;2;2;2;5;3;3;5;4;7;6;3;3;3;3;4;2;2;3;2;2;3;7;2;2;3;3;3;3;3;3;3;2;3;2;3;2;2;3;2;3;3;3;3;5;3;2;2;3;3;3;2;2;2;2;2;2;2;2;6;5;10;3;6;5;3;3;3;2;2;2;3;3;2;2;2;5;3;2;2;3;3;4;3;3;3;3;3;3;2;3;3;2;2;4;7;2;2;3;2;3;3;3;3;3;4;3;3;3;3;3;8;2;3;4;5;5;3;3;3;5;2;2;2;2;2;2;5;2;3;4;3;3;5;3;2;2;3;5;3;2;3;3;2;3;2;3;2;2;3;2;3;2;5;3;3;2;3;10;3;5;2;2;4;5;3;3;3;3;2;2;2;2;3;2;2;9;3;2;2;2;2;2;2;5;3;3;3;3;2;3;2;3;3;4;3;6;3;6;5;6;3;3;3;3;2;3;3;3;3;3;3;5;3;2;3;4;3;2;2;3;4;3;5;4;2;2;2;2;2;3;2;3;3;5;3;5;3;3;2;2;2;2;3;3;3;3;2;3;3;3;3;2;2;5;2;6;2;2;3;3;3;3;3;3;3;3;4;4;3;3;4;3;5;3;3;2;2;2;3;2;3;4;3;2;3;3;3;9;3;3;5;3;3;3;3;2;2;3;3;3;2;2;5;5;3;3;5;3;3;3;4;2;4;3;3;3;5;2;2;3;3;4;2;2;3;3;3;6;3;5;2;2;3;3;2;3;2;7;2;2;3;3;3;3;3;3;3;2;2;2;3;3;4;3;3;2;2;2;3;2;3;2;3;3;3;6;3;3;3;4;3;3;3;3;5;4;5;5;3;4;5;5;2;3;2;6;2;3;3;2;2;2;3;3;2;3;3;3;3;3;2;2;2;2;3;3;3;3;2;3;5;5;8;3;4;4;3;5;4;4;3;5;5;2;5;3;3;3;6;3;3;5;5;3;2;3;3;3;4;7;2;2;5;3;2;2;2;2;2;4;3;5;3;3;6;3;6;3;4;5;3;3;3;3;3;2;3;2;2;3;2;2;3;3;3;5;3;5;3;3;3;3;3;3;7;3;2;2;2;2;3;2;5;3;3;8;2;3;3;3;2;3;3;4;3;3;3;3;3;3;3;3;4;2;4;4;3;3;3;3;2;5;3;3;3;3;3;3;2;2;3;3;5;3;3;3;3;2;2;2;2;3;4;2;3;3;7;3;3;4;3;4;5;3;5;2;2;7;2;2;2;3;2;3;3;3;4;2;3;3;5;3;2;2;2;2;2;2;3;5;9;3;3;3;2;2;7;4;3;3;3;2;3;3;3;3;3;3;3;8;4;3;3;5;5;3;5;7;3;3;2;6;2;3;2;2;4;3;4;3;3;4;3;4;3;3;3;5;3;3;3;8;3;5;3;3;3;2;4;2;2;2;3;3;3;3;3;5;3;2;2;3;3;2;2;2;2;2;3;2;5;3;3;3;2;3;5;2;3;3;3;3;2;2;2;3;3;3;3;2;2;2;3;5;5;3;7;2;2;3;3;3;4;3;3;2;2;3;5;3;3;2;2;3;3;3;2;2;3;3;2;2;2;3;3;3;3;2;2;3;2;2;2;3;3;3;3;3;2;2;2;2;4;3;3;2;2;3;3;3;2;2;2;3;2;3;2;2;3;2;3;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;3;2;3;3;3;3;2;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;4;5;3;3;3;3;3;3;4;4;3;4;8;2;2;3;7;6;6;4;4;3;3;4;2;2;3;3;2;5;5;2;2;3;2;2;2;6;2;2;3;4;2;2;2;2;2;3;2;2;2;2;3;4;6;2;2;3;3;11;6;6;2;3;2;4;2;2;5;3;6;3;3;2;2;2;2;7;4;3;2;2;7;3;3;3;3;5;3;4;3;3;8;2;2;6;7;5;3;3;3;3;6;4;2;2;2;2;2;2;2;7;4;5;3;2;2;2;9;4;5;3;3;3;10;5;3;3;2;3;3;2;2;6;9;10;3;4;3;5;3;4;3;6;6;3;3;3;2;2;2;3;3;2;2;2;7;2;3;3;3;3;2;2;2;3;2;3;4;3;2;3;2;3;3;3;3;3;2;2;3;2;2;2;2;2;3;2;2;6;2;2;3;6;3;3;3;2;2;6;3;2;2;3;2;3;3;2;7;3;7;5;2;3;2;2;3;8;8;3;3;3;6;3;5;4;8;8;2;2;2;3;4;4;3;8;5;7;3;3;2;2;6;4;4;5;11;3;3;3;2;3;3;2;4;3;3;3;5;3;7;3;8;5;4;4;3;2;2;8;3;3;3;3;4;3;10;3;5;5;4;7;3;3;6;5;6;2;3;4;8;3;3;4;2;2;3;3;2;2;3;3;3;3;3;3;2;2;5;3;3;2;3;3;3;3;2;6;5;7;2;3;2;2;3;3;3;3;3;3;3;3;6;2;3;3;2;2;2;2;5;3;13;5;2;2;7;12;2;2;3;6;3;3;3;2;3;5;6;3;5;3;7;4;2;2;3;3;4;2;2;2;2;3;9;4;2;11;3;7;6;2;2;2;2;3;5;5;3;3;3;5;5;3;5;4;5;7;2;3;2;2;3;2;11;10;2;5;3;4;2;5;6;4;7;3;10;3;3;6;2;2;5;4;3;2;2;11;3;3;3;3;2;2;2;2;2;3;3;9;3;2;7;4;2;3;3;3;3;2;3;4;2;2;3;3;8;3;3;3;5;3;3;3;3;2;3;3;2;2;2;2;2;3;5;2;2;3;3;3;3;3;3;3;3;5;2;5;2;2;3;3;3;2;2;2;3;4;3;5;3;4;3;3;7;3;4;5;3;3;2;2;3;5;5;3;2;3;2;2;5;3;2;2;2;2;3;3;2;2;3;3;3;2;3;3;3;3;3;3;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;2;2;2;2;2;2;3;3;3;3;2;2;3;4;3;3;3;3;7;3;2;2;2;2;3;4;3;3;3;3;2;4;3;5;2;2;2;6;7;3;3;2;2;3;2;2;4;4;4;2;2;2;2;2;3;3;3;3;3;3;4;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;3;2;2;2;3;3;3;2;2;2;3;3;3;2;3;3;3;3;2;3;2;3;4;3;3;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;4;2;3;2;3;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;4;2;2;3;3;3;2;5;3;2;3;3;3;5;2;2;3;3;3;2;6;3;2;2;2;2;3;2;3;2;3;3;5;5;2;2;3;3;3;3;3;3;3;3;3;3;3;3;2;2;3;2;2;4;3;2;3;2;3;3;3;6;3;2;2;2;3;3;4;3;2;2;3;2;2;3;3;2;3;3;2;2;3;3;3;3;2;2;3;3;3;2;2;4;4;3;3;2;2;3;4;3;3;3;2;2;2;3;3;3;2;2;5;3;3;5;2;2;2;3;2;2;2;2;3;3;3;3;2;2;3;6;2;2;2;3;2;2;2;4;4;3;3;6;3;3;3;4;3;3;3;2;3;3;2;2;2;4;3;2;3;2;3;7;2;2;2;3;3;3;3;2;2;3;3;3;3;5;4;3;2;2;2;2;2;2;4;3;2;2;2;2;4;3;3;3;2;5;3;5;3;3;3;2;2;2;2;2;2;2;4;5;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;5;3;3;3;3;3;5;4;3;3;3;4;2;2;3;2;5;2;3;2;2;2;2;3;4;2;5;2;2;3;3;3;2;2;2;2;2;2;2;2;3;3;3;3;3;2;2;3;3;5;2;2;2;2;3;4;3;3;7;4;3;3;4;3;3;3;6;3;2;2;2;2;3;3;5;2;4;3;3;2;2;3;2;3;5;3;5;3;6;5;3;3;5;5;4;2;3;2;2;3;3;3;5;3;5;11;3;2;3;2;2;2;2;2;3;3;3;3;3;3;4;2;2;3;4;2;2;2;2;3;7;2;2;2;2;2;2;3;2;3;3;2;2;3;2;2;3;3;2;3;3;3;4;3;3;3;4;3;3;2;3;3;4;2;2;3;2;3;3;2;3;4;2;2;5;5;4;2;2;2;2;2;4;3;4;3;2;2;2;5;3;3;3;3;3;3;2;3;2;2;2;2;3;4;7;3;2;2;3;5;3;8;2;3;4;3;6;3;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;7;5;8;5;3;3;3;3;3;3;6;3;4;4;2;3;3;5;3;4;2;3;2;2;3;10;3;5;2;4;8;7;2;2;2;3;3;2;2;2;2;2;2;2;3;2;3;2;4;3;3;4;3;3;3;4;4;2;4;4;2;2;3;6;2;2;2;3;3;3;3;3;2;3;3;5;2;3;2;3;4;3;7;3;3;2;2;2;3;3;2;2;2;2;2;4;3;4;3;3;3;2;2;2;2;2;2;3;3;3;3;3;3;3;3;3;3;2;3;3;3;4;2;2;2;2;3;3;2;2;3;3;2;3;3;3;5;5;3;3;4;2;3;3;3;3;2;2;2;2;3;3;3;3;4;4;2;2;3;3;2;7;3;3;3;4;3;3;3;7;2;3;4;5;5;2;2;3;4;6;3;2;2;2;3;6;2;2;5;2;2;9;3;2;2;2;2;3;7;3;3;4;2;4;3;3;10;2;2;2;3;3;3;5;2;2;2;2;2;3;3;2;2;2;2;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;3;3;3;5;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;3;4;4;3;3;3;3;2;2;2;2;2;3;2;4;3;5;4;2;3;2;2;3;3;3;3;4;3;3;2;3;2;2;2;2;2;3;3;4;3;2;2;2;3;3;3;3;3;3;4;3;3;3;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;2;2;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;3;3;3;3;3;2;2;2;2;5;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;4;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;3;3;2;2;2;2;2;2;5;5;5;4;4;2;5;2;5;3;2;2;2;7;8;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;3;3;2;2;2;2;2;2;2;3;3;3;2;2;2;2;3;3;2;2;3;3;3;3;3;3;3;3;2;2;3;3;3;2;2;4;2;2;3;3;2;2;3;2;2;3;3;4;3;3;2;2;2;3;3;5;4;5;3;3;2;3;2;2;3;3;2;2;3;3;4;2;3;3;2;2;2;3;3;3;3;3;3;4;2;2;4;2;3;2;3;3;3;3;3;2;3;3;2;3;2;2;2;2;3;2;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;3;6;4;3;5;3;3;2;2;2;2;2;2;3;2;2;3;3;3;3;2;2;3;3;3;3;3;4;3;2;2;3;3;4;2;3;3;3;3;3;2;2;4;3;2;2;3;2;2;3;2;2;2;3;3;3;2;2;3;2;2;4;3;2;5;5;4;3;3;3;2;3;2;2;2;2;2;3;3;3;3;2;3;2;2;2;4;3;3;4;3;3;4;2;2;3;3;5;3;3;3;2;2;2;3;2;2;3;3;2;2;2;2;2;3;3;3;3;3;2;2;2;2;3;3;3;3;3;3;3;3;3;2;2;2;2;3;3;3;3;2;3;3;3;2;2;4;2;2;2;2;3;3;3;2;3;3;3;3;3;3;3;2;2;2;3;2;3;2;3;3;3;2;2;2;2;3;3;2;2;3;3;8;3;7;3;3;2;3;3;3;2;2;2;2;2;2;3;2;2;2;2;2;3;3;5;3;3;3;2;3;2;3;3;3;3;2;3;4;2;4;3;2;2;3;3;3;5;3;3;4;3;3;2;2;2;2;2;2;2;2;3;2;2;2;3;4;3;4;3;2;2;3;3;5;3;3;2;2;2;3;3;2;2;2;3;3;3;2;2;2;3;3;3;4;5;3;3;3;3;2;2;2;2;2;2;2;2;3;6;2;2;2;3;2;2;3;3;3;2;2;2;2;3;2;2;3;2;3;2;3;2;3;3;2;2;5;3;3;3;3;3;3;2;2;2;5;7;3;2;3;4;4;5;3;4;2;2;2;2;3;2;3;3;2;2;3;3;3;3;3;3;3;3;2;2;2;3;2;2;3;3;3;3;3;3;2;3;3;3;2;3;3;2;2;2;2;2;3;3;3;5;4;2;2;2;2;3;3;2;2;3;3;2;2;2;2;4;3;3;2;3;3;4;3;3;3;3;3;2;3;3;2;2;3;3;4;6;3;3;2;5;3;3;3;3;3;3;4;3;3;3;2;2;2;2;4;2;3;3;3;2;3;4;2;2;2;2;2;2;3;3;3;3;2;2;2;2;3;3;2;2;5;3;2;2;4;3;3;2;2;3;4;2;3;2;2;4;3;3;3;3;5;2;2;2;2;3;4;4;3;3;3;3;3;3;4;3;3;2;2;3;3;7;5;3;3;3;3;3;3;3;4;3;3;3;3;3;3;2;3;3;3;2;2;3;2;2;3;2;2;2;2;2;2;2;3;3;2;2;3;3;2;3;2;2;2;3;3;3;2;3;3;3;3;3;2;3;3;3;3;2;3;3;3;3;3;3;3;3;3;2;2;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;2;3;2;3;3;3;3;3;2;2;3;3;6;5;3;6;3;4;3;3;3;3;2;2;2;2;5;2;5;5;3;4;3;3;2;3;3;2;5;5;3;3;3;3;2;2;3;3;3;3;2;2;2;2;3;2;2;3;3;3;2;2;3;3;3;3;3;2;3;3;3;3;2;2;2;3;2;2;2;3;2;2;2;3;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;3;3;2;2;3;3;4;3;2;3;3;2;2;3;3;2;4;2;2;3;3;2;2;2;3;2;2;2;3;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;4;3;3;3;3;3;3;3;4;3;2;2;3;3;3;2;2;2;2;3;2;2;2;3;3;2;2;2;3;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;6;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;2;2;2;5;3;4;2;2;2;2;2;3;2;3;3;4;2;2;3;2;2;2;2;3;2;3;4;3;3;3;3;5;3;3;3;3;3;3;3;3;2;3;3;3;2;5;2;3;2;2;2;3;3;3;3;2;5;3;5;2;2;2;4;4;3;3;3;11;3;3;4;3;2;3;2;2;2;3;3;3;3;3;3;2;7;3;3;3;2;2;2;2;3;4;2;2;2;2;3;3;3;3;3;2;2;2;4;3;2;2;2;2;2;2;3;3;3;3;2;3;3;5;3;3;2;5;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;3;3;4;3;7;3;3;3;3;3;5;3;3;2;2;3;3;3;3;2;2;2;2;2;2;5;3;3;2;3;3;5;2;3;7;2;2;4;3;4;2;3;4;3;3;3;7;2;5;3;3;3;4;3;3;3;3;3;5;5;3;4;5;6;3;5;5;2;5;6;3;2;3;7;3;2;4;2;2;2;3;3;2;2;2;5;3;3;3;2;2;2;6;3;4;3;4;2;3;7;2;4;3;6;3;5;3;3;6;3;3;3;3;2;3;3;3;4;2;3;3;2;2;2;3;2;2;2;2;2;3;2;3;6;3;5;3;5;3;3;2;3;6;3;6;3;3;3;3;2;11;3;3;6;4;3;5;2;2;4;4;3;2;2;5;3;2;3;3;2;2;3;4;3;3;4;3;5;2;2;2;2;3;3;7;3;2;2;2;2;2;5;2;3;3;3;3;11;3;3;2;2;2;2;2;5;5;2;2;3;2;2;3;3;3;3;3;2;3;2;3;2;4;2;3;2;2;3;6;2;2;3;4;3;3;2;2;5;5;2;2;4;3;5;3;3;3;2;3;3;4;5;3;3;2;2;3;3;4;5;4;2;2;2;3;2;2;2;3;3;3;3;3;3;3;3;3;2;2;3;2;3;2;3;3;2;2;2;2;3;2;2;3;3;3;2;7;3;3;3;3;10;4;3;4;3;2;5;3;3;2;2;3;3;3;3;14;2;2;3;3;2;2;2;2;8;3;3;3;4;5;2;3;4;4;3;3;3;3;4;3;5;2;2;3;2;2;3;2;3;3;2;3;2;2;2;2;3;3;3;2;2;2;2;3;2;2;2;2;3;3;3;2;3;3;3;2;4;2;2;2;2;2;2;2;4;2;5;2;3;7;3;6;2;3;3;2;3;3;3;2;2;2;3;3;6;5;5;3;2;2;4;2;2;2;6;3;3;2;3;3;7;3;3;3;2;2;3;3;2;2;2;3;2;2;2;4;3;2;3;2;2;2;2;2;2;2;5;3;3;3;3;3;3;3;5;3;2;3;4;2;3;2;3;2;2;2;2;3;3;3;2;3;3;2;2;2;2;2;2;2;2;2;3;4;3;3;3;3;9;4;3;2;2;3;3;5;3;2;2;2;2;2;3;3;2;2;4;5;3;2;2;3;2;3;3;3;2;3;7;3;4;2;2;3;2;3;3;3;4;3;3;2;2;5;5;2;3;2;3;4;3;3;3;3;3;5;4;3;3;6;3;3;3;5;2;2;2;2;2;3;2;3;3;3;3;3;3;2;2;2;2;3;2;3;3;3;2;2;5;3;3;2;5;7;3;2;5;3;3;3;7;4;2;2;2;3;3;2;2;2;3;3;3;3;3;2;3;3;3;3;2;3;3;3;3;2;4;4;3;3;2;3;2;2;4;3;3;3;8;3;2;2;2;2;2;2;3;2;2;3;2;2;5;3;3;2;2;2;3;4;2;4;5;5;5;3;3;3;3;3;2;2;3;3;2;2;2;3;3;2;3;2;2;3;2;2;2;2;2;4;2;3;2;3;4;2;2;2;2;3;2;2;3;2;2;3;2;2;5;3;2;2;3;4;2;3;3;6;2;2;3;2;3;2;2;2;2;3;4;2;2;3;3;2;2;2;3;2;2;3;3;2;8;3;2;3;3;3;5;2;2;2;3;4;2;2;2;2;2;2;2;2;2;3;11;2;2;2;2;2;4;3;2;3;10;3;2;2;3;2;4;3;4;4;2;2;3;2;2;4;2;2;3;3;2;2;2;3;3;3;3;3;4;3;3;2;3;2;3;2;2;2;5;3;2;2;2;7;4;2;3;2;2;2;2;2;2;2;2;2;3;7;3;4;3;2;2;5;3;5;3;3;3;3;5;2;3;3;3;2;2;4;2;2;2;2;5;2;2;2;2;2;7;5;3;3;3;3;4;3;5;3;3;3;2;2;5;3;3;2;3;3;2;5;4;5;6;2;2;3;2;8;5;4;10;16;9;3;2;2;2;3;2;2;2;4;3;4;3;5;3;2;2;3;2;2;2;4;3;3;3;9;4;3;2;3;3;2;3;2;2;2;2;2;2;2;5;2;3;2;2;2;3;5;5;3;15;3;3;2;2;2;2;2;3;3;2;2;5;6;3;3;3;2;2;3;3;9;3;5;5;3;3;3;2;8;2;2;2;2;3;3;2;2;2;2;3;4;5;8;3;2;3;2;3;2;4;4;3;3;2;2;2;2;3;2;3;2;2;3;2;3;2;2;3;2;2;3;7;2;2;3;5;2;2;3;2;3;2;2;3;2;3;2;2;2;3;2;3;4;10;2;2;4;8;3;3;5;3;5;3;5;3;4;2;3;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;5;3;5;2;3;2;3;2;5;3;2;2;2;3;2;2;2;3;3;3;3;3;3;2;2;4;5;2;2;3;3;2;3;3;4;3;3;4;5;2;3;2;7;3;3;3;2;2;2;2;2;2;3;3;3;3;5;3;3;3;7;3;5;5;3;3;2;2;3;4;3;3;3;3;2;2;3;2;2;2;3;3;3;3;2;3;3;2;9;3;2;2;2;2;2;3;2;2;3;3;3;3;3;3;2;3;4;7;3;5;3;7;5;3;3;3;3;4;3;4;3;3;3;3;4;3;3;2;4;2;2;4;3;3;3;3;3;3;5;3;2;2;2;4;3;3;6;3;3;2;3;2;3;4;6;5;3;3;3;4;13;2;2;3;6;2;2;2;3;3;3;3;3;3;3;3;2;2;5;3;2;2;3;2;2;2;3;3;3;3;3;3;3;2;2;3;3;3;2;3;3;5;5;3;5;2;2;2;2;3;5;5;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;2;3;4;2;4;3;3;3;3;2;2;2;2;2;2;3;3;3;5;2;2;3;2;3;2;2;3;2;2;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;3;2;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;5;3;3;3;2;2;8;3;3;3;5;5;3;3;3;3;2;2;2;4;4;4;2;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;3;5;5;3;4;2;4;3;3;2;2;2;2;2;2;2;3;5;2;2;3;3;4;2;2;3;2;3;3;2;2;3;2;2;2;8;3;10;3;3;4;5;3;3;3;3;2;5;2;2;5;2;2;5;3;5;5;5;3;5;4;3;2;3;3;3;2;2;2;2;3;3;2;2;3;4;2;2;2;2;3;5;2;2;2;2;3;3;4;5;2;2;2;2;5;3;2;2;2;2;3;4;3;4;2;2;4;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;7;3;3;2;2;2;2;2;2;4;3;3;2;5;3;3;7;6;3;2;2;3;2;2;3;3;3;3;3;3;2;2;2;2;2;3;3;3;2;3;3;3;6;3;3;4;2;2;2;2;2;2;2;2;2;2;3;5;4;2;2;2;2;2;2;2;2;2;2;2;5;2;3;3;3;2;2;2;2;2;3;2;2;3;3;3;4;8;2;2;2;5;2;2;3;2;3;3;3;2;2;3;2;3;2;2;2;2;2;2;2;2;3;3;2;2;3;3;3;3;3;2;3;5;2;14;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;3;3;2;7;5;5;3;2;4;2;2;4;3;2;2;2;3;3;3;4;3;3;3;3;4;3;3;3;2;2;2;2;2;2;2;3;3;2;4;2;2;2;2;2;2;2;3;3;2;2;4;2;2;2;2;2;2;2;2;2;4;2;3;2;2;2;2;2;2;2;4;3;2;3;2;2;3;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;5;3;2;2;11;10;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;6;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;3;2;2;2;2;3;4;2;3;2;3;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;4;2;3;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;4;3;2;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;4;2;4;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;5;5;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;4;4;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;4;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;4;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;4;2;2;2;2;2;3;2;2;2;2;3;3;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;3;5;5;2;3;2;2;2;2;2;3;3;2;2;2;2;3;3;3;6;3;5;4;2;2;2;2;2;2;2;2;3;2;2;3;3;3;3;5;2;2;3;2;5;3;2;2;4;3;4;3;7;5;4;5;3;4;2;5;4;5;3;3;2;2;3;3;5;2;2;3;3;2;2;2;3;2;2;2;2;2;4;3;3;3;3;8;2;2;2;2;2;2;2;2;3;3;4;2;2;2;4;2;2;3;3;3;3;3;3;4;2;3;3;3;3;3;2;2;3;3;3;2;2;3;2;2;3;2;2;2;2;2;3;3;3;5;4;3;2;3;3;3;3;3;3;2;5;3;2;3;2;2;2;3;3;4;2;2;2;3;2;7;3;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;3;3;2;2;4;2;5;3;5;3;3;3;3;3;3;3;3;2;2;3;3;3;2;3;3;2;3;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;3;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;3;3;4;2;4;3;3;4;2;2;4;3;4;2;2;3;5;3;3;3;3;3;2;3;2;2;3;3;3;3;3;3;3;6;3;3;3;3;4;3;3;3;3;3;5;3;3;3;3;2;2;2;2;2;3;2;3;2;2;2;2;5;3;4;3;2;2;2;3;2;3;3;3;3;2;2;2;3;2;2;2;2;2;2;2;2;3;5;3;3;3;3;3;4;3;3;3;4;3;5;3;3;2;3;5;2;2;3;5;2;3;3;3;3;3;2;3;5;3;3;5;3;2;3;3;3;3;3;2;3;7;3;3;5;3;2;3;7;3;5;4;2;2;3;3;2;2;3;3;3;3;3;3;3;3;3;2;2;3;7;3;3;5;2;3;3;2;2;2;2;3;3;3;4;4;4;5;4;5;3;4;3;3;2;2;3;3;5;3;3;3;7;3;2;2;5;3;3;3;2;3;3;2;2;2;3;2;2;5;3;3;4;3;2;2;3;3;4;3;3;3;3;2;3;2;2;2;3;3;3;2;2;3;2;4;4;2;3;3;2;3;3;2;2;3;3;3;5;2;2;3;3;2;2;3;3;3;3;3;3;2;2;4;3;2;3;3;3;3;2;5;3;2;2;2;2;2;4;3;3;5;2;5;3;3;2;3;3;3;3;3;3;3;2;2;3;4;3;3;3;3;3;2;2;3;3;3;3;2;2;3;3;3;3;3;3;2;4;2;3;2;2;4;5;3;2;2;2;2;2;3;2;2;2;2;3;3;2;2;2;3;3;3;3;5;3;7;3;3;3;2;3;3;2;3;5;3;3;3;3;3;3;3;3;2;5;5;3;5;2;3;2;3;2;3;3;3;3;3;2;2;3;2;7;3;2;3;3;2;2;3;2;3;3;2;5;4;2;3;2;3;5;2;2;5;4;5;3;3;4;2;2;4;3;2;2;3;2;3;3;5;3;5;2;3;3;3;3;3;3;2;3;3;2;2;3;2;2;2;4;3;3;3;3;3;5;3;5;5;3;7;3;3;5;2;3;3;6;5;4;3;3;3;2;2;3;2;3;3;2;2;2;3;4;3;3;3;3;2;2;3;2;3;3;5;4;5;3;3;3;3;5;2;5;3;2;2;2;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;3;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;3;8;2;3;3;5;3;3;7;4;3;3;2;2;2;2;2;3;2;3;4;3;3;2;3;2;3;3;3;3;3;2;2;2;2;2;3;2;3;3;2;3;2;3;3;2;3;4;3;4;3;3;3;3;2;2;3;2;3;3;2;3;3;4;5;3;5;3;3;2;2;3;2;2;2;4;3;6;5;2;2;4;4;3;7;3;2;2;3;3;3;2;3;3;3;3;3;3;3;3;3;3;2;2;3;3;2;2;2;2;3;4;3;2;2;3;2;2;3;3;3;3;3;3;5;2;2;3;2;2;11;3;3;3;2;2;3;2;2;2;2;2;5;5;3;3;5;3;10;3;5;4;11;2;2;3;5;2;3;3;3;3;3;3;2;3;3;4;3;3;3;3;2;2;5;3;3;3;2;2;4;5;6;3;4;3;3;2;2;2;2;3;3;3;3;2;2;3;2;3;3;2;2;2;2;2;2;2;2;3;2;2;3;2;3;2;2;3;3;2;2;3;2;2;2;2;3;3;2;3;2;2;2;2;2;3;6;2;2;2;2;2;3;3;3;3;3;2;2;8;3;7;5;3;3;3;3;2;3;3;5;5;4;3;9;3;5;4;4;4;3;2;6;3;3;3;2;2;2;2;2;2;2;3;5;5;2;3;3;2;2;2;2;3;3;2;2;2;3;2;2;2;2;3;3;3;3;3;2;2;2;4;2;2;2;3;3;5;3;5;3;6;3;2;2;2;2;2;2;3;2;3;3;3;5;2;2;2;2;3;2;2;2;2;3;3;3;3;3;3;3;2;2;2;3;2;2;3;4;4;4;3;2;2;3;3;3;2;2;3;4;3;3;3;3;3;3;3;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;3;2;3;3;3;2;3;3;3;2;2;3;2;2;2;3;3;3;2;4;3;2;2;3;4;3;3;7;3;3;3;2;2;2;2;2;3;3;2;4;2;2;3;4;4;4;3;4;3;3;3;5;3;5;2;3;2;3;2;3;3;3;5;3;3;4;3;3;2;5;3;2;3;3;5;3;5;3;3;2;2;3;5;3;2;2;3;4;3;3;3;3;7;3;4;3;4;5;3;2;2;3;3;3;3;3;3;2;2;2;3;4;5;3;3;6;4;3;2;2;3;3;5;2;2;3;3;4;3;3;3;3;2;2;2;2;3;3;3;3;3;3;3;2;2;2;2;3;2;2;3;3;3;3;3;2;2;2;3;2;2;3;3;2;2;2;2;7;2;2;2;2;3;3;3;3;4;3;2;2;3;7;5;3;4;7;4;3;3;3;3;3;3;3;5;3;3;3;3;2;2;3;3;3;3;5;2;2;2;2;5;5;3;3;5;3;4;3;3;3;3;3;6;3;3;3;4;4;3;3;3;3;3;2;2;5;6;3;2;2;2;2;3;3;3;2;3;3;2;3;4;3;2;2;2;3;2;2;2;2;2;2;3;2;2;3;3;2;2;3;2;3;2;3;3;3;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;2;2;3;2;3;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;4;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;4;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;5;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;3;2;2;2;6;6;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;3;3;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;3;3;2;2;4;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;3;2;3;2;3;2;2;3;2;2;3;5;2;2;2;3;2;2;2;2;2;2;2;5;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;6;6;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;3;2;2;2;3;3;3;2;2;2;2;2;3;3;3;4;2;2;2;2;2;3;3;3;3;3;2;3;3;3;3;3;3;3;3;3;3;3;3;3;2;2;2;3;2;2;2;2;3;4;3;4;4;3;3;3;3;3;3;3;3;3;3;3;3;3;3;2;3;2;4;3;2;2;2;2;2;2;2;3;3;3;2;2;2;2;3;3;3;2;2;2;3;5;3;2;4;5;3;3;2;2;3;3;3;3;3;2;3;3;2;2;2;3;3;2;3;3;4;3;3;3;3;3;3;3;3;2;2;2;2;3;4;2;2;2;3;2;3;2;2;2;3;2;3;2;3;3;2;2;2;2;3;3;3;2;2;2;3;2;3;4;3;3;3;2;2;3;3;2;2;3;3;3;2;2;2;3;3;3;4;2;3;3;2;3;3;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;3;3;3;3;3;3;3;3;3;3;3;3;3;5;3;3;3;2;2;4;2;2;2;2;3;3;5;3;2;2;2;2;2;2;3;3;3;2;2;3;5;3;3;3;4;3;3;3;5;3;4;3;3;3;3;4;2;2;2;2;4;3;3;3;3;3;3;3;3;3;3;2;4;4;2;2;4;3;3;2;4;5;4;2;2;3;4;3;3;6;3;3;3;6;2;2;3;3;3;3;3;3;3;2;2;2;2;3;2;4;3;3;2;2;3;2;3;2;2;3;3;5;3;3;2;3;3;8;3;12;3;3;3;2;2;2;2;3;2;3;2;2;2;2;3;2;3;4;3;2;3;3;2;2;2;2;2;3;3;3;4;3;3;2;3;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;3;3;2;2;3;2;3;3;2;2;2;2;2;2;2;3;3;2;2;3;3;3;2;2;2;2;6;5;3;2;2;2;2;2;2;2;3;2;3;3;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;3;3;3;3;4;2;2;2;3;3;2;3;3;2;2;2;3;2;2;2;3;2;3;3;2;2;3;3;2;2;2;2;2;2;3;3;2;3;3;3;2;2;2;2;2;3;3;3;3;3;3;3;3;3;4;3;3;2;2;2;3;3;3;3;3;2;3;3;3;3;3;3;3;3;2;2;3;3;5;3;2;2;4;2;2;2;3;3;3;2;4;3;2;2;3;3;3;2;2;2;2;2;2;3;3;3;3;3;5;2;2;2;2;4;3;2;2;3;2;2;2;2;3;3;6;3;3;3;3;4;3;3;3;4;3;3;2;2;3;4;3;3;3;6;3;3;3;3;3;3;5;3;2;2;2;2;2;3;2;3;3;3;3;3;2;2;2;2;3;3;3;2;2;2;2;2;2;2;3;3;2;2;2;3;2;2;2;2;2;2;2;2;2;3;3;4;3;3;3;3;2;2;3;3;5;3;3;2;3;3;3;4;3;3;2;3;3;3;2;3;3;3;3;2;4;2;3;2;2;2;2;2;3;4;3;3;3;3;2;3;2;2;2;2;3;2;2;3;3;3;3;4;3;3;2;3;3;2;2;3;3;4;3;3;3;3;2;3;2;3;3;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;4;3;3;3;3;3;3;3;2;3;2;2;2;2;2;2;2;2;3;2;3;3;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;3;2;2;3;3;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;3;3;2;3;3;3;3;3;2;2;2;2;2;2;2;2;3;2;2;3;3;3;2;3;2;2;3;2;2;3;3;3;3;3;3;3;2;2;3;2;3;3;2;2;3;3;3;3;3;3;3;2;3;2;3;3;3;3;3;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;2;3;2;2;2;3;3;2;2;2;3;2;3;2;3;4;3;4;3;4;2;3;3;7;2;2;2;3;3;2;2;3;2;3;3;2;2;2;2;3;3;3;4;3;2;2;2;6;3;3;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;3;3;2;3;2;2;3;2;2;3;3;3;3;2;2;2;3;3;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;3;2;2;3;3;3;5;3;2;2;3;2;3;3;2;2;2;3;2;3;2;3;3;3;2;2;2;2;3;3;3;3;3;3;2;2;3;2;3;2;3;2;2;3;3;2;2;2;2;2;4;3;2;3;2;3;3;2;3;3;3;2;3;2;3;3;3;3;3;3;3;3;2;2;3;2;5;2;2;2;2;2;3;3;2;2;5;3;3;3;2;2;3;3;3;2;3;2;3;2;2;2;2;3;2;2;3;3;3;3;2;3;3;3;3;3;2;3;2;3;2;2;3;3;3;3;3;2;2;3;3;4;3;3;3;3;3;3;4;3;3;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;2;3;3;3;5;3;3;3;3;2;3;3;3;3;3;4;3;3;2;2;2;3;3;3;5;2;2;3;2;3;3;3;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;4;3;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;3;2;3;2;3;3;3;3;3;3;3;2;4;3;2;3;3;2;2;2;3;2;3;3;3;3;2;3;3;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;3;4;3;5;5;3;3;3;3;2;2;3;3;2;2;3;3;2;3;4;3;4;4;5;4;3;2;2;2;3;2;2;2;2;2;2;7;3;2;3;3;3;3;2;2;2;2;5;3;2;3;3;3;2;3;3;2;2;2;2;2;3;3;3;3;2;3;2;2;2;2;2;3;4;2;2;2;5;3;3;3;3;5;4;4;3;4;3;5;3;3;3;3;3;3;2;2;3;5;3;2;3;3;3;2;2;2;2;3;3;3;2;3;5;3;2;2;3;2;3;3;3;2;2;2;2;3;2;2;3;3;2;2;5;3;3;2;2;3;3;3;3;3;3;3;3;3;3;2;2;2;3;3;3;3;3;2;2;2;2;3;3;3;2;2;2;2;2;2;3;2;2;2;2;3;3;3;3;3;4;3;3;3;3;2;2;3;2;2;2;2;3;3;2;2;2;2;3;2;3;3;3;3;5;3;2;2;3;2;3;2;2;2;2;2;2;2;3;3;3;3;3;3;3;3;3;2;2;2;2;2;2;3;2;3;2;2;2;2;3;3;2;2;2;2;3;3;2;2;3;3;3;3;3;3;3;3;3;2;2;3;3;3;3;2;2;5;2;2;2;3;3;2;2;2;2;2;3;3;3;3;3;2;3;2;3;4;2;3;3;2;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;3;2;2;3;4;2;2;3;3;2;2;3;3;3;3;2;2;3;2;2;3;3;3;3;2;2;2;2;2;3;3;4;2;2;3;3;3;2;3;3;3;3;3;3;2;2;3;2;3;3;3;3;3;3;3;3;3;3;3;2;2;2;2;3;3;7;4;3;3;3;3;3;3;3;2;2;2;3;3;3;3;3;3;3;3;3;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;5;3;3;3;2;2;3;3;2;2;3;4;3;3;3;2;2;2;3;3;3;2;2;2;2;3;2;2;3;3;2;3;3;3;2;2;2;3;2;2;2;3;2;2;3;3;5;3;3;2;2;3;2;3;2;2;3;5;3;3;3;2;3;3;3;3;2;2;2;3;3;3;3;3;3;3;3;3;3;3;2;2;3;3;3;3;3;2;3;3;3;3;3;3;4;3;3;3;3;3;3;3;2;2;3;5;3;3;3;3;2;3;2;3;3;2;2;2;2;3;2;2;2;3;2;2;3;3;4;3;3;3;4;3;3;4;2;4;2;2;3;3;3;3;3;2;2;2;2;2;2;2;3;2;3;2;5;5;2;3;3;3;3;2;2;3;3;2;2;4;3;3;3;3;3;2;2;2;2;2;2;2;2;3;2;2;3;3;3;3;3;2;2;3;3;3;3;3;2;2;3;2;2;11;3;2;2;4;5;3;3;3;5;3;11;3;3;2;2;3;3;7;3;4;2;2;4;2;3;3;2;2;2;2;2;2;2;3;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;3;3;3;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;3;2;3;3;3;3;3;3;3;3;3;2;5;3;3;3;8;3;2;2;2;2;5;3;2;2;3;3;3;3;2;3;2;3;2;3;3;5;3;2;3;3;6;3;2;2;2;2;5;3;2;2;2;2;2;2;5;4;4;2;2;3;2;3;2;5;2;3;5;3;2;2;2;2;4;3;5;3;6;3;3;6;5;3;6;4;3;3;3;4;4;5;4;5;5;3;7;2;7;3;3;2;3;3;2;4;5;3;2;2;3;3;4;6;2;2;2;2;2;4;5;3;5;3;5;4;4;4;2;3;4;2;6;4;3;2;3;3;3;3;5;7;9;2;2;3;3;6;8;5;4;3;3;3;3;7;5;3;3;2;2;2;2;3;3;4;5;3;3;3;5;3;2;2;2;2;4;3;2;2;3;2;2;5;2;2;2;2;8;5;3;2;2;4;8;2;2;2;2;2;3;7;12;2;3;3;9;2;3;3;3;3;3;2;2;3;2;2;4;3;3;3;2;2;4;2;2;2;2;2;3;3;2;3;3;2;2;3;3;4;2;6;3;8;5;2;2;2;2;2;2;3;2;2;2;2;4;3;2;2;3;3;3;5;3;3;7;7;3;3;3;5;2;2;2;2;2;2;3;2;3;3;2;2;5;6;4;6;3;2;2;2;2;2;3;3;3;2;2;8;2;2;3;3;4;3;2;4;4;2;2;3;2;4;2;2;6;3;4;10;3;5;3;3;2;2;3;3;3;2;3;3;2;2;2;2;5;3;6;7;3;2;5;10;3;3;3;2;3;4;3;5;2;2;2;2;2;2;15;3;9;4;5;3;3;3;2;3;2;3;3;3;2;3;3;3;2;4;5;9;2;3;3;2;3;3;3;3;3;3;9;3;2;2;3;7;3;3;2;6;5;3;2;2;2;2;3;3;4;7;5;5;5;5;6;5;3;3;3;5;3;6;3;5;3;5;4;3;3;3;3;5;7;3;3;3;3;3;2;2;2;3;3;5;3;3;5;3;5;3;3;5;3;5;5;7;3;3;3;3;2;4;3;2;3;6;3;3;2;2;6;3;5;3;2;2;3;3;3;2;3;3;6;8;5;2;2;3;3;2;2;3;6;7;4;3;3;3;3;2;2;2;2;5;7;5;3;3;4;6;5;3;3;5;5;3;4;4;5;5;3;5;2;2;3;2;3;5;3;5;6;3;3;2;9;3;3;3;4;3;3;8;2;2;3;2;2;4;3;3;3;2;3;3;3;3;3;3;3;5;3;7;2;4;3;3;5;5;3;5;3;3;2;2;3;3;5;6;3;2;5;3;2;2;3;5;2;2;3;3;8;3;8;2;2;3;3;4;3;3;6;3;3;3;3;3;3;3;4;2;3;2;3;4;3;3;2;3;3;3;2;3;6;2;2;2;2;2;2;2;2;3;2;2;6;3;4;9;4;2;2;3;7;3;6;2;2;3;4;3;6;3;3;4;2;2;2;4;2;2;3;3;5;7;4;2;2;3;2;3;3;3;3;3;3;4;3;3;6;4;2;3;2;2;2;2;2;2;3;3;3;3;3;7;2;3;3;2;3;2;3;2;3;3;5;6;4;3;3;2;3;5;2;6;3;3;3;3;3;2;3;2;2;5;2;3;3;3;3;4;2;3;2;3;3;4;3;2;3;2;2;2;6;5;3;3;3;3;3;3;3;5;2;3;3;3;2;2;3;3;2;2;2;2;2;3;2;2;2;3;3;6;3;2;2;3;5;5;3;2;2;10;3;3;3;3;3;5;5;3;2;2;3;3;5;3;2;2;4;2;4;2;2;5;3;3;2;3;2;4;3;3;2;2;4;3;6;2;2;3;4;3;4;6;6;2;2;5;2;2;2;3;3;2;2;2;2;6;3;3;7;3;3;5;3;4;3;4;3;3;3;2;3;2;2;2;3;2;4;3;5;7;3;3;3;4;6;7;3;7;3;2;3;9;3;9;4;2;3;2;2;3;3;3;2;3;2;3;3;2;3;3;3;3;3;3;6;4;3;3;2;3;4;5;3;4;3;3;2;3;4;2;6;2;2;2;5;5;3;5;4;4;3;6;3;3;3;3;6;3;3;2;3;4;3;4;5;3;3;6;5;3;2;2;3;5;2;8;6;3;5;3;4;3;3;3;3;4;3;3;3;5;6;2;2;3;3;3;4;4;6;2;2;3;2;2;2;3;2;3;2;2;3;3;2;3;2;2;3;3;2;2;2;6;4;5;2;7;4;5;3;7;2;3;2;2;2;3;2;2;3;3;3;3;2;3;4;3;4;4;12;3;2;3;7;3;3;3;3;5;3;3;2;2;2;2;2;2;2;2;2;5;2;8;3;3;6;3;3;3;8;5;5;9;3;3;2;4;3;3;3;6;3;2;2;2;2;2;2;2;2;2;2;5;2;2;4;2;2;3;2;2;2;2;2;2;2;2;3;2;3;2;2;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;5;2;3;3;2;3;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;3;2;3;2;4;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;4;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;4;5;2;2;2;2;2;2;2;2;2;5;2;2;2;4;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;3;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;4;3;3;3;3;3;2;2;2;4;2;2;2;3;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;3;2;3;5;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;3;3;2;2;2;2;2;3;2;2;2;2;2;2;2;4;2;2;2;2;2;5;2;2;2;2;2;2;3;3;2;3;2;2;6;2;2;6;2;5;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;3;2;2;3;3;2;3;3;2;2;3\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/data/rle_class_ratio17.csv",
    "content": "0;0;0;0;0;0;0;0;0;1;-1;0;1;-1;2;0;0;0;1;0;1;0;0;-4;0;0;0;0;0;1;0;2;-3;0;1;-1;1;-1;1;0;0;0;0;-1;1;0;0;-1;1;-1;2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;0;0;1;0;0;0;0;0;0;0;4;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;-1;0;1;0;0;0;0;0;0;0;0;0;0;-5;0;0;0;1;-1;-1;1;1;-1;0;0;0;0;0;1;-1;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;-1;1;0;0;0;0;1;-1;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;2;-2;0;2;-2;0;0;-1;1;0;2;-2;0;0;0;0;0;0;0;0;0;2;0;-2;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;0;0;0;1;0;2;-2;0;0;-1;1;0;0;0;0;0;0;0;0;0;0;0;2;0;-2;-1;1;0;0;0;0;0;0;0;0;2;-2;0;0;0;1;0;-2;0;0;2;-1;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;2;-2;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;2;-2;2;-2;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;-1;0;1;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;2;-3;3;-2;0;-1;1;2;-2;0;2;-1;-1;0;0;0;0;-1;1;0;0;-1;1;0;-1;1;2;-2;0;0;0;0;-1;1;-1;2;-1;0;0;0;-1;1;0;0;2;-2;0;2;-2;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;-1;0;1;-1;1;0;0;0;0;-1;3;-1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;1;-1;0;0;0;0;1;1;-2;0;0;0;0;0;1;-2;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;1;-1;0;2;-2;0;0;0;0;0;0;0;0;2;-3;1;0;0;0;0;0;0;0;0;0;0;0;0;0;2;0;0;-2;2;-2;0;0;0;0;0;0;-1;1;0;0;-1;0;0;1;0;-1;0;1;0;0;0;0;0;0;-1;3;-2;0;0;0;0;2;-1;-1;0;2;-2;1;1;-2;0;-1;0;1;2;-2;0;0;1;1;-2;0;1;-1;0;0;0;0;0;-1;0;1;-1;1;0;0;0;0;0;2;-1;-1;0;0;-1;0;0;0;1;0;0;2;-2;0;-1;0;1;0;0;0;0;0;2;-2;0;0;0;2;-2;0;1;-1;0;1;-2;1;0;0;-1;2;-1;2;-2;1;-1;2;-2;-1;2;-1;0;0;0;0;0;0;1;-1;1;-1;1;-1;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;0;2;-2;0;0;-1;0;1;0;1;-1;0;0;0;0;0;1;-1;0;0;-1;1;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;2;-2;0;2;-1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;2;-2;-1;1;0;-1;1;0;0;2;-2;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;-1;1;-1;2;1;-1;-1;1;0;-1;0;0;0;0;0;0;0;0;0;0;0;1;1;-2;0;0;0;0;0;0;0;0;2;-2;1;0;-1;-1;1;1;1;-1;-1;2;0;-2;0;0;0;1;-1;0;0;0;0;0;2;-2;0;0;0;-1;0;1;0;0;0;1;-1;-1;0;3;-2;-1;1;0;2;-3;1;0;0;0;0;0;0;0;0;0;0;2;-2;1;1;-3;1;0;1;-1;0;0;2;0;-2;0;0;0;0;2;-2;0;0;-1;0;1;0;0;2;-2;-1;1;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;0;0;-1;1;-1;0;0;0;0;3;-3;1;0;0;0;0;0;0;0;0;0;-1;0;2;-2;0;1;0;0;0;0;0;-1;1;-1;0;0;1;1;1;-2;0;0;0;0;1;-1;1;-2;1;0;0;-1;1;1;1;-2;-1;1;1;-1;0;2;-2;-1;1;1;-1;2;-2;0;0;-1;1;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;-1;0;0;0;1;0;0;-1;1;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;1;-1;0;0;0;0;1;-1;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;-1;1;2;-2;0;0;0;0;0;0;2;-2;2;-2;-1;1;0;0;0;0;0;0;0;0;2;0;-2;0;1;-1;2;-1;-1;0;0;0;0;0;1;-1;1;-1;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;2;-2;0;0;0;0;2;0;-2;1;-1;0;0;0;0;1;1;-2;0;0;0;0;0;2;-2;2;-2;1;1;-2;0;0;1;1;-1;-1;0;0;2;-3;0;0;1;-1;1;0;-1;2;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;0;-2;0;2;-2;0;0;0;0;0;0;0;-1;3;-3;1;2;-2;0;0;0;0;2;-2;0;-1;0;1;0;-1;1;0;0;1;-1;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;1;1;0;-2;0;2;-2;0;0;0;2;-2;0;0;0;2;-2;0;0;0;0;-1;2;-1;0;0;0;0;0;1;-1;2;0;-1;0;1;0;-3;3;-2;2;0;0;0;0;-1;1;-2;1;-1;1;-2;1;0;0;0;0;2;0;-2;0;0;0;0;0;2;-2;0;0;-1;0;0;3;-2;1;0;0;0;1;-2;2;-2;1;0;-1;0;0;-1;0;1;0;2;0;-3;0;3;-2;2;-2;2;-2;0;1;-1;2;-2;-1;1;0;0;0;1;-2;1;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;-1;1;-1;2;-2;0;0;0;0;0;0;0;0;0;-1;3;0;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;1;-1;0;-1;1;0;2;-2;0;0;0;0;0;0;1;-1;0;-1;1;1;-1;0;0;0;0;1;-1;2;-2;0;1;-1;0;2;0;-2;0;0;0;-1;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;1;-1;-1;0;1;0;0;0;2;-2;0;0;0;0;1;0;-1;2;0;0;-1;-1;0;0;0;0;0;0;0;-1;1;0;0;0;0;0;0;2;0;0;-1;-2;2;0;0;-1;0;0;0;1;0;-1;0;0;0;0;0;-1;0;1;2;-2;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-1;-1;0;1;-1;0;0;0;0;0;0;0;-1;3;-2;0;0;0;0;0;0;0;0;0;0;0;-1;1;0;0;0;2;-2;0;0;0;0;0;2;-1;1;-2;0;-1;3;-3;1;-1;0;3;-3;0;1;0;0;1;1;-2;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;1;-1;0;0;0;2;-2;0;0;0;0;1;-1;0;0;0;0;0;0;0;-1;2;-1;0;0;0;0;0;0;0;0;0;0;0;1;0;-1;0;2;-3;1;0;0;0;0;0;0;0;0;0;-1;2;-1;0;0;0;0;0;0;0;2;0;-2;0;1;0;-1;1;-1;0;0;1;-1;0;0;0;0;1;-1;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;2;-2;0;0;0;0;2;-2;0;0;0;-1;0;1;0;0;0;0;1;0;-1;0;-1;1;0;0;1;1;-2;2;0;-2;0;1;-1;0;2;0;0;-1;-1;1;-1;-1;1;0;1;-1;2;-2;0;0;0;2;-2;2;-2;0;0;0;0;0;0;0;-1;0;0;0;0;0;0;0;0;0;0;0;1;0;-1;0;1;2;-1;1;-2;0;0;0;0;0;0;0;0;0;0;1;1;0;-1;-1;2;-2;0;2;-2;0;2;-2;1;-1;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;2;-2;0;0;0;0;0;0;0;0;0;1;0;-1;1;1;-2;-1;1;-1;1;0;0;0;0;1;-1;0;-1;1;0;0;0;0;1;-1;0;-1;1;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;2;-2;2;-2;0;-1;1;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;2;-2;2;-3;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;-2;2;-1;0;0;1;-1;0;0;0;0;0;1;1;-2;0;0;1;0;-1;-1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;1;-2;0;0;0;0;0;0;0;1;-1;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-1;2;-1;0;2;-2;2;-2;0;-1;1;0;0;0;-1;0;1;-1;3;0;-1;-1;-1;0;0;0;0;3;-3;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;5;0;0;0;1;-1;0;0;0;0;-5;0;0;1;-2;2;-1;1;-1;0;0;0;0;1;-1;0;0;0;0;0;0;0;0;-1;1;-1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;2;-2;0;0;0;0;0;0;0;0;-1;1;0;0;1;-1;0;-1;0;1;0;0;0;0;0;0;-1;1;0;1;-1;0;0;-1;0;1;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;5;-6;0;0;1;-1;1;0;0;-1;1;0;-1;1;-1;0;2;0;0;0;0;0;0;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;-4;0;0;0;0;0;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;1;0;0;0;0;-2;0;2\n"
  },
  {
    "path": "java/mlt-core/src/jmh/java/org/maplibre/mlt/data/rle_id_ratio22_45k.csv",
    "content": "2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;5;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;3;2;3;2;2;2;2;2;5;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;6;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;4;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;7;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;5;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;3;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;5;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;3;2;2;3;3;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;4;3;4;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;5;5;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;3;3;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;3;4;5;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;8;8;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;3;3;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;5;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;3;2;3;2;2;2;2;2;5;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;6;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;4;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;7;7;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;5;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;3;2;3;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;5;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;3;2;2;3;3;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;4;4;3;4;2;2;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;5;5;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;4;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;3;2;2;2;2;2;2;3;3;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;3;3;4;5;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;5;5;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;3;2;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;8;8;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;2;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;3;2;2;3;2;2;3;3;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;4;4;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;5;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;2;2;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;2;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;3;3;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2;2\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/compare/CompareHelper.java",
    "content": "package org.maplibre.mlt.compare;\n\nimport jakarta.annotation.Nullable;\nimport java.util.Comparator;\nimport java.util.HashSet;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.SequencedCollection;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport lombok.Builder;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.jetbrains.annotations.NotNull;\nimport org.locationtech.jts.geom.Geometry;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.data.Property;\nimport org.maplibre.mlt.util.StreamUtil;\n\npublic final class CompareHelper {\n  private CompareHelper() {}\n\n  public enum CompareMode {\n    None,\n    Layers,\n    Geometry,\n    Properties,\n    All\n  }\n\n  @Builder\n  public record Difference(\n      @NotNull String message,\n      @Nullable Integer layerIndex,\n      @Nullable String layerName,\n      @Nullable Integer featureIndex,\n      @Nullable Pair<@NotNull String, @NotNull String> items,\n      @Nullable Pair<@NotNull Geometry, @NotNull Geometry> geometries) {\n    @Override\n    public String toString() {\n      final var itemStr =\n          (items != null) ? (\"MVT: \" + items.getLeft() + \" MLT: \" + items.getRight()) : \"\";\n      final var geomStr =\n          (geometries != null)\n              ? (\"MVT geometry:\\n\"\n                  + geometries.getLeft()\n                  + \"\\nMLT geometry:\\n\"\n                  + geometries.getRight())\n              : \"\";\n      return (message\n          + (itemStr.isEmpty() ? \"\" : \" \" + itemStr)\n          + (geomStr.isEmpty() ? \"\" : \"\\n\" + geomStr)\n          + ((layerIndex != null) ? (\" at layer index \" + layerIndex) : \"\")\n          + ((layerName != null) ? (\" in layer '\" + layerName + \"': \") : \"\")\n          + ((featureIndex != null) ? (\" at feature index \" + featureIndex) : \"\"));\n    }\n  }\n\n  /// Compare the content of MLT and MVT tiles.\n  /// Returns a single difference, stopping immediately when the first one is found.\n  /// @param mlTile The MLT tile\n  /// @param mbTile The MVT tile\n  /// @param compareMode Which parts of the tiles to compare\n  /// @return A description of the first difference found, or an empty Optional if the tiles are\n  // equal.\n  public static Optional<Difference> compareTiles(\n      @NotNull MapLibreTile mlTile,\n      @NotNull MapboxVectorTile mvTile,\n      @NotNull CompareMode compareMode) {\n    return compareTiles(mlTile, mvTile, compareMode, null, false);\n  }\n\n  /// Compare the content of MLT and MVT tiles.\n  /// Returns a single difference, stopping immediately when the first one is found.\n  /// @param mlTile The MLT tile\n  /// @param mbTile The MVT tile\n  /// @param compareMode Which parts of the tiles to compare\n  /// @param layerFilter A regex pattern to filter layers by name. If null, all layers are compared.\n  /// @param filterInvert If true, only layers *not*  matching the filter are compared.\n  /// @return A description of the first difference found, or an empty Optional if the tiles are\n  // equal.\n  public static Optional<Difference> compareTiles(\n      @NotNull MapLibreTile mlTile,\n      @NotNull MapboxVectorTile mvTile,\n      @NotNull CompareMode compareMode,\n      @Nullable Pattern layerFilter,\n      boolean filterInvert) {\n    final Predicate<Layer> filter =\n        (layerFilter == null)\n            ? x -> true\n            : x -> layerFilter.matcher(x.name()).matches() ^ filterInvert;\n    return compareTiles(mlTile, mvTile, compareMode, filter);\n  }\n\n  /// Compare the content of MLT and MVT tiles\n  /// Returns a single difference, stopping immediately when the first one is found.\n  /// @param mlTile The MLT tile\n  /// @param mbTile The MVT tile\n  /// @param compareMode Which parts of the tiles to compare\n  /// @param layerFilter A filter to select which layers to compare.\n  /// @return A description of the first difference found, or an empty Optional if the tiles are\n  // equal.\n  public static Optional<Difference> compareTiles(\n      @NotNull MapLibreTile mlTile,\n      @NotNull MapboxVectorTile mvTile,\n      @NotNull CompareMode compareMode,\n      @NotNull Predicate<Layer> layerFilter) {\n    final var mvtLayers =\n        mvTile.getLayerStream().filter(x -> !x.features().isEmpty()).filter(layerFilter).toList();\n    final var mltLayers =\n        mlTile.getLayerStream().filter(x -> !x.features().isEmpty()).filter(layerFilter).toList();\n    if (mltLayers.size() != mvtLayers.size()) {\n      final var mvtNames = mvtLayers.stream().map(Layer::name).collect(Collectors.joining(\", \"));\n      final var mltNames = mltLayers.stream().map(Layer::name).collect(Collectors.joining(\", \"));\n      return Optional.of(\n          Difference.builder()\n              .message(\"Number of layers in MLT and MVT tiles do not match\")\n              .items(Pair.of(mvtNames, mltNames))\n              .build());\n    }\n    for (var i = 0; i < mvtLayers.size(); i++) {\n      final var mltLayer = mltLayers.get(i);\n      final var mvtLayer = mvtLayers.get(i);\n      final var layerResult = compareLayer(mltLayer, mvtLayer, compareMode, i);\n      if (layerResult.isPresent()) {\n        return layerResult;\n      }\n    }\n    return Optional.empty();\n  }\n\n  private static Optional<Difference> compareLayer(\n      Layer mltLayer, Layer mvtLayer, @NotNull CompareMode compareMode, int layerIndex) {\n    final var mltFeatures = mltLayer.features();\n    final var mvtFeatures = mvtLayer.features();\n    if (!mltLayer.name().equals(mvtLayer.name())) {\n      return Optional.of(\n          Difference.builder()\n              .message(\"Layer names differ\")\n              .layerIndex(layerIndex)\n              .items(Pair.of(mvtLayer.name(), mltLayer.name()))\n              .build());\n    }\n    return compareFeatures(\n        mltFeatures, mvtFeatures, compareMode, layerIndex, mvtLayer.name(), true);\n  }\n\n  public static Optional<Difference> compareFeatures(\n      SequencedCollection<Feature> mltFeatures,\n      SequencedCollection<Feature> mvtFeatures,\n      @NotNull CompareMode compareMode,\n      int layerIndex,\n      String layerName,\n      boolean allowFeatureSort) {\n    if (mltFeatures.size() != mvtFeatures.size()) {\n      return Optional.of(\n          Difference.builder()\n              .message(\"Number of features differ\")\n              .items(\n                  Pair.of(String.valueOf(mvtFeatures.size()), String.valueOf(mltFeatures.size())))\n              .layerIndex(layerIndex)\n              .layerName(layerName)\n              .build());\n    }\n\n    // Allow features to be sorted by ID and still match if all features have IDs\n    final var sortableIDs =\n        allowFeatureSort\n            && mvtFeatures.stream().allMatch(Feature::hasId)\n            && mltFeatures.stream().allMatch(Feature::hasId);\n    final var maybeSortedMvtFeatures =\n        sortableIDs\n            ? mvtFeatures.stream().sorted(Comparator.comparing(Feature::getId))\n            : mvtFeatures.stream();\n    final var maybeSortedMltFeatures =\n        sortableIDs\n            ? mltFeatures.stream().sorted(Comparator.comparing(Feature::getId))\n            : mltFeatures.stream();\n\n    return StreamUtil.zip(\n            maybeSortedMltFeatures,\n            maybeSortedMvtFeatures,\n            (mltFeature, mvtFeature) ->\n                compareFeature(\n                    mltFeature, mvtFeature, compareMode, mltFeature.getIndex(), layerName))\n        .filter(Optional::isPresent)\n        .map(Optional::get)\n        .findFirst();\n  }\n\n  private static Optional<Difference> compareFeature(\n      Feature mltFeature,\n      Feature mvtFeature,\n      @NotNull CompareMode compareMode,\n      int featureIndex,\n      String layerName) {\n    if (!Objects.equals(mvtFeature.idOrNull(), mltFeature.idOrNull())) {\n      return Optional.of(\n          Difference.builder()\n              .message(\"Feature IDs differ\")\n              .layerName(layerName)\n              .featureIndex(featureIndex)\n              .items(\n                  Pair.of(String.valueOf(mvtFeature.getId()), String.valueOf(mltFeature.getId())))\n              .build());\n    }\n    if (compareMode == CompareMode.Geometry || compareMode == CompareMode.All) {\n      final var geomResult = compareGeometry(mltFeature, mvtFeature, featureIndex, layerName);\n      if (geomResult.isPresent()) {\n        return geomResult;\n      }\n    }\n    if (compareMode == CompareMode.Properties || compareMode == CompareMode.All) {\n      final var propResult = compareProperties(mltFeature, mvtFeature, featureIndex, layerName);\n      if (propResult.isPresent()) {\n        return propResult;\n      }\n    }\n    return Optional.empty();\n  }\n\n  private static Optional<Difference> compareGeometry(\n      Feature mltFeature, Feature mvtFeature, int featureIndex, String layerName) {\n    final var mltGeometry = mltFeature.getGeometry();\n    final var mltGeomValid = mltGeometry.isValid();\n    final var mvtGeometry = mvtFeature.getGeometry();\n    final var mvtGeomValid = mvtGeometry.isValid();\n    if (mltGeomValid != mvtGeomValid) {\n      return Optional.of(\n          Difference.builder()\n              .message(\"Geometry validity does not match\")\n              .layerName(layerName)\n              .featureIndex(featureIndex)\n              .items(\n                  Pair.of(mvtGeomValid ? \"valid\" : \"invalid\", mltGeomValid ? \"valid\" : \"invalid\"))\n              .build());\n    }\n\n    if (mvtGeomValid && !mltGeometry.equals(mvtGeometry)) {\n      return Optional.of(\n          Difference.builder()\n              .message(\"Geometries do not match\")\n              .layerName(layerName)\n              .featureIndex(featureIndex)\n              .geometries(Pair.of(mvtGeometry, mltGeometry))\n              .build());\n    }\n\n    return Optional.empty();\n  }\n\n  private static boolean propertyValuesEqual(Property pa, Property pb, int featureIndex) {\n    if (pa == null || pb == null) {\n      return pa == null && pb == null;\n    }\n\n    final var a = pa.getValue(featureIndex);\n    final var b = pb.getValue(featureIndex);\n\n    // Try simple equality\n    if (Objects.equals(a, b)) {\n      return true;\n    }\n    if (a == null || b == null) {\n      return false;\n    }\n    // Allow for, e.g., int32 and int64 representations of the same number by comparing strings\n    return a.toString().equals(b.toString());\n  }\n\n  private static Optional<Difference> compareProperties(\n      Feature mltFeature, Feature mvtFeature, int featureIndex, String layerName) {\n    final var nonNullMVTKeys =\n        mvtFeature\n            .getPropertyStream()\n            .filter(p -> p.getValue(featureIndex) != null)\n            .map(Property::getName)\n            .collect(Collectors.toSet());\n\n    final var nonNullMLTKeys =\n        mltFeature\n            .getPropertyStream()\n            .filter(p -> p.getValue(featureIndex) != null)\n            .map(Property::getName)\n            .collect(Collectors.toSet());\n    // compare keys\n    if (!nonNullMVTKeys.equals(nonNullMLTKeys)) {\n      final var mvtKeys = getAsymmetricSetDiff(nonNullMVTKeys, nonNullMLTKeys);\n      final var mvtKeyStr = mvtKeys.isEmpty() ? \"(none)\" : String.join(\", \", mvtKeys);\n      final var mltKeys = getAsymmetricSetDiff(nonNullMLTKeys, nonNullMVTKeys);\n      final var mltKeyStr = mltKeys.isEmpty() ? \"(none)\" : String.join(\", \", mltKeys);\n      return Optional.of(\n          Difference.builder()\n              .message(\"Property keys do not match\")\n              .layerName(layerName)\n              .featureIndex(featureIndex)\n              .items(Pair.of(mvtKeyStr, mltKeyStr))\n              .build());\n    }\n    // compare values\n    final var unequalKeys =\n        mvtFeature\n            .getPropertyStream()\n            .filter(\n                p ->\n                    !propertyValuesEqual(\n                        p, mltFeature.findProperty(p.getName()).orElse(null), featureIndex))\n            .map(Property::getName)\n            .toList();\n    if (!unequalKeys.isEmpty()) {\n      final var mvtValues =\n          unequalKeys.stream()\n              .map(\n                  key ->\n                      key\n                          + \": \"\n                          + mvtFeature\n                              .findProperty(key)\n                              .map(p -> p.getValue(featureIndex))\n                              .map(String::valueOf)\n                              .orElse(\"null\"))\n              .collect(Collectors.joining(\", \"));\n      final var mltValues =\n          unequalKeys.stream()\n              .map(\n                  key ->\n                      key\n                          + \": \"\n                          + mltFeature\n                              .findProperty(key)\n                              .map(p -> p.getValue(featureIndex))\n                              .map(String::valueOf)\n                              .orElse(\"null\"))\n              .collect(Collectors.joining(\", \"));\n      return Optional.of(\n          Difference.builder()\n              .message(\"Property values do not match\")\n              .layerName(layerName)\n              .featureIndex(featureIndex)\n              .items(Pair.of(mvtValues, mltValues))\n              .build());\n    }\n    return Optional.empty();\n  }\n\n  /// Returns the values that are in set `a` but not in set `b`\n  private static <T> Set<T> getAsymmetricSetDiff(Set<T> a, Set<T> b) {\n    Set<T> diff = new HashSet<>(a);\n    diff.removeAll(b);\n    return diff;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/CollectionUtils.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport java.util.Collection;\n\npublic class CollectionUtils {\n  private CollectionUtils() {}\n\n  public static int[] unboxInts(Collection<? extends Number> values) {\n    int i = 0;\n    int[] result = new int[values.size()];\n    for (var value : values) {\n      result[i++] = value.intValue();\n    }\n    return result;\n  }\n\n  public static long[] unboxLongs(Collection<? extends Number> values) {\n    int i = 0;\n    long[] result = new long[values.size()];\n    for (var value : values) {\n      result[i++] = value.longValue();\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/ColumnMapping.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport jakarta.annotation.Nullable;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.regex.Pattern;\n\n/*\n * In the converter it is currently possible to map a set of feature properties into a nested struct with a depth of one level.\n * For example a set of name:* feature properties like name:de and name:en can be mapped into a name struct.\n * This has the advantage that the dictionary (Shared Dictionary Encoding) can be shared among the nested columns.\n * */\npublic class ColumnMapping {\n  public Pattern getPrefix() {\n    return prefix;\n  }\n\n  public Pattern getDelimiter() {\n    return delimiter;\n  }\n\n  public boolean getUseSharedDictionaryEncoding() {\n    return useSharedDictionaryEncoding;\n  }\n\n  public boolean hasColumnNames() {\n    return columnNames != null && !columnNames.isEmpty();\n  }\n\n  public Collection<String> getColumnNames() {\n    return columnNames;\n  }\n\n  /// Construct a mapping based on common prefixes and a delimiter\n  public ColumnMapping(Pattern prefix, Pattern delimiter, boolean useSharedDictionaryEncoding) {\n    this(prefix, delimiter, null, useSharedDictionaryEncoding);\n  }\n\n  public ColumnMapping(String prefix, String delimiter, boolean useSharedDictionaryEncoding) {\n    this(\n        Pattern.compile(prefix, Pattern.LITERAL),\n        Pattern.compile(delimiter, Pattern.LITERAL),\n        null,\n        useSharedDictionaryEncoding);\n  }\n\n  ///  Construct a mapping based on explicit column names\n  public ColumnMapping(Collection<String> columnNames, boolean useSharedDictionaryEncoding) {\n    this(null, null, columnNames, useSharedDictionaryEncoding);\n  }\n\n  private ColumnMapping(\n      Pattern prefix,\n      Pattern delimiter,\n      Collection<String> columnNames,\n      boolean useSharedDictionaryEncoding) {\n    this.prefix = prefix;\n    this.delimiter = delimiter;\n    this.useSharedDictionaryEncoding = useSharedDictionaryEncoding;\n    this.columnNames = (columnNames == null) ? Collections.emptyList() : List.copyOf(columnNames);\n  }\n\n  public boolean isMatch(String propertyName) {\n    if (hasColumnNames()) {\n      return columnNames.contains(propertyName);\n    } else {\n      // In the prefix case, the prefix alone or a prefix+delimiter+suffix match\n      final var prefixMatcher = prefix.matcher(propertyName);\n      if (prefixMatcher.find()) {\n        if (prefixMatcher.start() == 0) {\n          final var remainder = propertyName.substring(prefixMatcher.end());\n          final var suffixMatcher = delimiter.matcher(remainder);\n          return remainder.isEmpty() || (suffixMatcher.find() && suffixMatcher.start() == 0);\n        }\n      }\n      return false;\n    }\n  }\n\n  /// Find a matching column mapping among a collection of mappings grouped by layer name patterns\n  public static @Nullable ColumnMapping findMapping(\n      ColumnMappingConfig patternMappings, String layerName, String propertyName) {\n    return patternMappings.entrySet().stream()\n        .filter(entry -> entry.getKey().matcher(layerName).matches())\n        .map(entry -> findMapping(entry.getValue(), propertyName))\n        .filter(Objects::nonNull)\n        .findFirst()\n        .orElse(null);\n  }\n\n  /// Find a matching column mapping among a collection of mappings\n  public static @Nullable ColumnMapping findMapping(\n      Collection<ColumnMapping> columnMappings, String propertyName) {\n    return columnMappings.stream().filter(m -> m.isMatch(propertyName)).findFirst().orElse(null);\n  }\n\n  @Override\n  public String toString() {\n    if (hasColumnNames()) {\n      return String.format(\n          \"{columns=%s dictionary=%s}\",\n          String.join(\", \", columnNames), useSharedDictionaryEncoding);\n    } else {\n      return String.format(\n          \"{prefix=%s separator=%s dictionary=%s}\", prefix, delimiter, useSharedDictionaryEncoding);\n    }\n  }\n\n  private final Pattern prefix;\n  private final Pattern delimiter;\n  private final boolean useSharedDictionaryEncoding;\n  private final Collection<String> columnNames;\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/ColumnMappingConfig.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport org.jetbrains.annotations.NotNull;\n\n/// Specifies multiple column mappings for different layers based on regex patterns\n@SuppressWarnings(\"serial\")\npublic class ColumnMappingConfig extends LinkedHashMap<Pattern, List<ColumnMapping>> {\n  public ColumnMappingConfig() {\n    super();\n  }\n\n  public static ColumnMappingConfig of(\n      @NotNull Pattern pattern, @NotNull List<ColumnMapping> columnMappings) {\n    final var config = new ColumnMappingConfig();\n    config.put(pattern, columnMappings);\n    return config;\n  }\n\n  public @Override String toString() {\n    final var sb = new StringBuilder();\n    for (final var entry : this.entrySet()) {\n      final var pattern = entry.getKey();\n      final var mappings = entry.getValue();\n      final var mappingStrings =\n          mappings.stream().map(Object::toString).collect(Collectors.joining(\", \"));\n      sb.append(pattern).append(\" : \").append(mappingStrings);\n    }\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/ConversionConfig.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport jakarta.annotation.Nullable;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport lombok.Builder;\nimport lombok.Getter;\nimport lombok.experimental.Accessors;\nimport org.jetbrains.annotations.NotNull;\n\n@Builder(builderClassName = \"ConfigBuilder\", toBuilder = true)\n@Accessors(fluent = true)\npublic class ConversionConfig {\n  @NotNull @Builder.Default @Getter private final Boolean includeIds = DEFAULT_INCLUDE_IDS;\n  @NotNull @Builder.Default @Getter private final Boolean useFastPFOR = DEFAULT_USE_FAST_PFOR;\n  @NotNull @Builder.Default @Getter private final Boolean useFSST = DEFAULT_USE_FSST;\n\n  @NotNull @Builder.Default @Getter\n  private final TypeMismatchPolicy typeMismatchPolicy = DEFAULT_MISMATCH_POLICY;\n\n  @NotNull @Builder.Default @Getter\n  private final Map<String, FeatureTableOptimizations> optimizations = Map.of();\n\n  @NotNull @Builder.Default @Getter\n  private final Boolean preTessellatePolygons = DEFAULT_PRE_TESSELLATE_POLYGONS;\n\n  @NotNull @Builder.Default @Getter\n  private final Boolean useMortonEncoding = DEFAULT_USE_MORTON_ENCODING;\n\n  @NotNull @Builder.Default @Getter private final List<String> outlineFeatureTableNames = List.of();\n  @Nullable @Builder.Default @Getter private final Pattern layerFilterPattern = null;\n\n  @NotNull @Builder.Default @Getter\n  private final Boolean layerFilterInvert = DEFAULT_LAYER_FILTER_INVERT;\n\n  @NotNull @Builder.Default @Getter\n  private final IntegerEncodingOption integerEncodingOption = DEFAULT_INTEGER_ENCODING;\n\n  @NotNull @Builder.Default @Getter\n  private final IntegerEncodingOption geometryEncodingOption = DEFAULT_INTEGER_ENCODING;\n\n  public static class ConfigBuilder {\n    // Allow SyntheticMltUtil to extend the builder for testing purposes\n    public ConfigBuilder() {}\n  }\n\n  public enum TypeMismatchPolicy {\n    COERCE, // Coerce values to string on type mismatch\n    ELIDE, // Skip values that don't match the first type encountered\n    FAIL // Throw an error if a type mismatch is detected (default)\n  }\n\n  public enum IntegerEncodingOption {\n    AUTO, // Automatically select best encoding (default)\n    PLAIN, // Force plain encoding\n    DELTA, // Force delta encoding\n    RLE, // Force RLE encoding (only for const streams)\n    DELTA_RLE // Force delta-RLE encoding\n  }\n\n  public static final boolean DEFAULT_INCLUDE_IDS = true;\n  public static final boolean DEFAULT_USE_FAST_PFOR = false;\n  public static final boolean DEFAULT_USE_FSST = false;\n  public static final TypeMismatchPolicy DEFAULT_MISMATCH_POLICY = TypeMismatchPolicy.FAIL;\n  public static final boolean DEFAULT_USE_MORTON_ENCODING = true;\n  public static final boolean DEFAULT_PRE_TESSELLATE_POLYGONS = false;\n  public static final boolean DEFAULT_LAYER_FILTER_INVERT = false;\n  public static final IntegerEncodingOption DEFAULT_INTEGER_ENCODING = IntegerEncodingOption.AUTO;\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/FeatureTableOptimizations.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport java.util.List;\n\n/**\n * Configure optimizations for a Layer\n *\n * @param allowSorting Specifies if the FeatureTable can be sorted or the specified order has to be\n *     preserved.\n * @param allowIdRegeneration Specifies if the Ids can be reassigned to a feature since they have no\n *     special meaning and only has to meet the MVT spec criteria. The value of the id SHOULD be\n *     unique among the features of the parent layer.\n * @param columnMappings Mapping of flat MVT properties to a nested MLT column to apply shared\n *     dictionary encoding among the nested columns.\n */\npublic record FeatureTableOptimizations(\n    boolean allowSorting, boolean allowIdRegeneration, List<ColumnMapping> columnMappings) {}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/MltConverter.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport com.google.gson.Gson;\nimport jakarta.annotation.Nullable;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.SequencedCollection;\nimport java.util.TreeMap;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\nimport org.apache.commons.lang3.NotImplementedException;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.converter.encodings.GeometryEncoder;\nimport org.maplibre.mlt.converter.encodings.MltTypeMap;\nimport org.maplibre.mlt.converter.encodings.PropertyEncoder;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.LayerSource;\nimport org.maplibre.mlt.data.Property;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\n\npublic class MltConverter {\n  /// Create tileset metadata from source data\n  /// Note that this method will read through all features of all layers to infer the column\n  /// data types, nullability, etc., it's preferable to construct the metadata from a schema.\n  /// @param layerSource The input tile to create metadata from\n  /// @param columnMappingConfig Optional column mapping configuration\n  /// @param includeIdIfPresent Whether to include an ID column\n  public static MltMetadata.TileSetMetadata createTilesetMetadata(\n      @NotNull LayerSource layerSource,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      boolean includeIdIfPresent) {\n    // TODO: Allow determining whether ID is present automatically\n    return createTilesetMetadata(\n        layerSource,\n        ConversionConfig.TypeMismatchPolicy.FAIL,\n        columnMappingConfig,\n        includeIdIfPresent);\n  }\n\n  /// Create tileset metadata from source data\n  /// See {@link #createTilesetMetadata(LayerSource, ColumnMappingConfig, boolean)}\n  /// @param layerSource The input tile to create metadata from\n  /// @param columnMappingConfig Optional column mapping configuration to be applied to all layers\n  /// @param includeIdIfPresent Whether to include an ID column\n  /// @param typeMismatchPolicy Policy for handling type mismatches\n  /// first type encountered is used)\n  public static MltMetadata.TileSetMetadata createTilesetMetadata(\n      @NotNull LayerSource layerSource,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      boolean includeIdIfPresent,\n      @NotNull ConversionConfig.TypeMismatchPolicy typeMismatchPolicy) {\n    final var config = ConversionConfig.builder().typeMismatchPolicy(typeMismatchPolicy).build();\n    return createTilesetMetadata(layerSource, config, columnMappingConfig, includeIdIfPresent);\n  }\n\n  /// Create tileset metadata from source data\n  /// See {@link #createTilesetMetadata(LayerSource, ColumnMappingConfig, boolean)}\n  /// @param layerSource The input tile to create metadata from\n  /// @param config Optional configuration\n  /// @param columnMappingConfig Optional column mapping configuration to be applied to all layers\n  /// @param includeIdIfPresent Whether to include an ID column\n  public static MltMetadata.TileSetMetadata createTilesetMetadata(\n      @NotNull LayerSource layerSource,\n      @Nullable ConversionConfig config,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      boolean includeIdIfPresent) {\n    return createTilesetMetadata(\n        layerSource,\n        (config != null) ? config.typeMismatchPolicy() : null,\n        columnMappingConfig,\n        includeIdIfPresent);\n  }\n\n  /// Create tileset metadata from source data\n  /// See {@link #createTilesetMetadata(LayerSource, ColumnMappingConfig, boolean)}\n  /// @param layerSource The input tile to create metadata from\n  /// @param typeMismatchPolicy Policy for handling type mismatches\n  /// @param columnMappingConfig Optional column mapping configuration to be applied to all layers\n  /// @param includeIdIfPresent Whether to include an ID column\n  public static MltMetadata.TileSetMetadata createTilesetMetadata(\n      @NotNull LayerSource layerSource,\n      @NotNull ConversionConfig.TypeMismatchPolicy typeMismatchPolicy,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      boolean includeIdIfPresent) {\n    final var tileset = new MltMetadata.TileSetMetadata();\n    tileset.featureTables =\n        layerSource\n            .getLayerStream()\n            .map(\n                layer ->\n                    createTilesetMetadata(\n                        layer, typeMismatchPolicy, columnMappingConfig, includeIdIfPresent))\n            .toList();\n    return tileset;\n  }\n\n  private static MltMetadata.FeatureTable createTilesetMetadata(\n      @NotNull Layer layer,\n      @NotNull ConversionConfig.TypeMismatchPolicy typeMismatchPolicy,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      boolean includeIdIfPresent) {\n    final LinkedHashMap<String, MltMetadata.Column> columnSchemas = new LinkedHashMap<>();\n    final LinkedHashMap<ColumnMapping, MltMetadata.ComplexField> complexPropertyColumnSchemas =\n        new LinkedHashMap<>();\n\n    var hasId = false;\n    var hasLongId = false;\n    var hasNullId = false;\n    var featureIndex = 0;\n    for (var feature : layer.features()) {\n      final var currentFeatureIndex = featureIndex;\n\n      feature\n          .getPropertyStream()\n          .forEach(\n              property -> {\n                resolveColumnType(\n                    property,\n                    layer.name(),\n                    currentFeatureIndex,\n                    columnMappingConfig,\n                    columnSchemas,\n                    complexPropertyColumnSchemas,\n                    typeMismatchPolicy);\n              });\n\n      if (includeIdIfPresent) {\n        if (feature.hasId()) {\n          hasId = true;\n          if ((!hasLongId && feature.getId() > Integer.MAX_VALUE)\n              || feature.getId() < Integer.MIN_VALUE) {\n            hasLongId = true;\n          }\n        } else {\n          hasNullId = true;\n        }\n      }\n      featureIndex++;\n    }\n\n    for (var complexPropertyColumnScheme : complexPropertyColumnSchemas.entrySet()) {\n      final var schema = complexPropertyColumnScheme.getValue();\n      final var result = resolveComplexColumnMapping(schema);\n      final var parentName = result.getLeft();\n\n      // Each complex column scheme needs to have a unique entry name in the column map, but there\n      // is no specific column to which it maps.  For now, just ensure that the value is unique.\n      final var column = createColumn(parentName, result.getRight());\n      IntStream.iterate(0, i -> i + 1)\n          .mapToObj(i -> parentName + i)\n          .filter(name -> !columnSchemas.containsKey(name))\n          .findFirst()\n          .ifPresent(name -> columnSchemas.put(name, column));\n    }\n\n    final var estimatedColumns = 2 + columnSchemas.size() + complexPropertyColumnSchemas.size();\n    final var featureTableSchema = new MltMetadata.FeatureTable(layer.name(), estimatedColumns);\n\n    // If present, `id` must be the first column\n    if (columnSchemas.values().stream().anyMatch(MltTypeMap.Tag0x01::isID)) {\n      throw new RuntimeException(\"Unexpected ID Column\");\n    }\n    if (hasId) {\n      featureTableSchema\n          .columns()\n          .add(\n              new MltMetadata.Column(\n                  new MltMetadata.Field(MltMetadata.idFieldType(hasLongId, hasNullId))));\n    }\n\n    // The `geometry` column is mandatory and has to be the first column after `ID`\n    featureTableSchema\n        .columns()\n        .add(createComplexColumnScheme(null, false, MltMetadata.ComplexType.GEOMETRY));\n\n    // Add the remaining items in name order for consistent output.\n    // Put complex columns after scalar columns to match old behavior.\n    columnSchemas.values().stream()\n        .sorted(\n            Comparator.comparing((MltMetadata.Column c) -> c.isComplex() ? 1 : 0)\n                .thenComparing(c -> c.getName()))\n        .forEach(featureTableSchema.columns()::add);\n\n    return featureTableSchema;\n  }\n\n  private static void resolveColumnType(\n      @NotNull Property property,\n      @NotNull String layerName,\n      int featureIndex,\n      @Nullable ColumnMappingConfig columnMappingConfig,\n      @NotNull LinkedHashMap<String, MltMetadata.Column> columnSchemas,\n      @NotNull LinkedHashMap<ColumnMapping, MltMetadata.ComplexField> complexColumnSchemas,\n      @NotNull ConversionConfig.TypeMismatchPolicy typeMismatchPolicy) {\n    final var sourcePropertyName = property.getName();\n\n    if (property.isNested()) {\n      throw new NotImplementedException(\"Nested property types are not yet supported\");\n    }\n    final var scalarField = property.getType().scalarType();\n    final var scalarType = (scalarField != null) ? scalarField.physicalType() : null;\n\n    // If this property already has a column...\n    final var previousSchema = columnSchemas.get(sourcePropertyName);\n    if (previousSchema != null) {\n      // Make sure the types match.\n      // If not, coercion or nullification must be enabled, and replace\n      // the column with a string column, if it isn't already.\n      if (previousSchema.isScalar()) {\n        if (previousSchema.field().type().scalarType().physicalType() != null) {\n          final var prevPhysicalType = previousSchema.field().type().scalarType().physicalType();\n          if (prevPhysicalType != scalarType) {\n            final var newSchema = checkUpgrade(previousSchema, scalarType);\n            if (newSchema != null) {\n              if (newSchema != previousSchema) {\n                columnSchemas.put(sourcePropertyName, newSchema);\n              }\n            } else if (typeMismatchPolicy == ConversionConfig.TypeMismatchPolicy.COERCE) {\n              if (prevPhysicalType != MltMetadata.ScalarType.STRING) {\n                columnSchemas.put(\n                    sourcePropertyName,\n                    new MltMetadata.Column(\n                        new MltMetadata.Field(\n                            MltMetadata.scalarFieldType(\n                                MltMetadata.ScalarType.STRING, previousSchema.isNullable()),\n                            previousSchema.getName())));\n              }\n            } else if (typeMismatchPolicy != ConversionConfig.TypeMismatchPolicy.ELIDE) {\n              throw new RuntimeException(\n                  String.format(\n                      \"Layer '%s' Feature index %d Property '%s' has different type: %s / %s\",\n                      layerName,\n                      featureIndex,\n                      property.getName(),\n                      scalarType.name(),\n                      prevPhysicalType.name()));\n            }\n            return;\n          }\n        }\n      }\n    }\n\n    if (columnMappingConfig != null) {\n      final var columnMapping =\n          ColumnMapping.findMapping(columnMappingConfig, layerName, sourcePropertyName);\n      if (columnMapping != null) {\n        // A mapping exists for this property.\n        // Create the parent type and add a child type entry.\n        final var parentColumn =\n            complexColumnSchemas.computeIfAbsent(columnMapping, k -> createComplexColumn());\n\n        if (parentColumn.children().stream().noneMatch(c -> c.name().equals(sourcePropertyName))) {\n          parentColumn\n              .children()\n              .add(createScalarFieldScheme(sourcePropertyName, true, scalarType));\n        }\n\n        return;\n      }\n    }\n\n    // no matching column mappings, create a plain scalar column\n    columnSchemas.put(\n        sourcePropertyName, createScalarColumnScheme(sourcePropertyName, true, scalarType));\n  }\n\n  private static MltMetadata.Column checkUpgrade(\n      MltMetadata.Column previousSchema, MltMetadata.ScalarType scalarType) {\n    final var prevPhysicalType = previousSchema.field().type().scalarType().physicalType();\n\n    if (prevPhysicalType == MltMetadata.ScalarType.INT_32\n        && scalarType == MltMetadata.ScalarType.INT_64) {\n      // Allow implicit upgrade from INT_32 to INT_64\n      return new MltMetadata.Column(\n          new MltMetadata.Field(\n              MltMetadata.scalarFieldType(\n                  MltMetadata.ScalarType.INT_64, previousSchema.isNullable()),\n              previousSchema.getName()));\n    } else if (prevPhysicalType == MltMetadata.ScalarType.INT_64\n        && scalarType == MltMetadata.ScalarType.INT_32) {\n      // no-op\n      // keep INT_64\n      return previousSchema;\n    } else if (prevPhysicalType == MltMetadata.ScalarType.FLOAT\n        && scalarType == MltMetadata.ScalarType.DOUBLE) {\n      // Allow implicit upgrade from FLOAT to DOUBLE\n      return new MltMetadata.Column(\n          new MltMetadata.Field(\n              MltMetadata.scalarFieldType(\n                  MltMetadata.ScalarType.DOUBLE, previousSchema.isNullable()),\n              previousSchema.getName()));\n    } else if (prevPhysicalType == MltMetadata.ScalarType.DOUBLE\n        && scalarType == MltMetadata.ScalarType.FLOAT) {\n      // no-op\n      // keep DOUBLE\n      return previousSchema;\n    }\n    return null;\n  }\n\n  /// Resolve complex column mapping by determining common prefix and adjusting child names\n  /// @return a pair of the resolved parent column name and the adjusted complex column scheme\n  private static Pair<String, MltMetadata.ComplexField> resolveComplexColumnMapping(\n      MltMetadata.ComplexField column) {\n    final var prefix =\n        StringUtils.getCommonPrefix(\n            column.children().stream().map(c -> c.name()).toArray(String[]::new));\n    if (!prefix.isEmpty()) {\n      var children =\n          column.children().stream()\n              .map(\n                  child -> {\n                    final var name = child.name();\n                    if (!name.startsWith(prefix)) {\n                      throw new RuntimeException(\n                          \"Unexpected column mapping: prefix is not present\");\n                    }\n                    return new MltMetadata.Field(child.type(), name.substring(prefix.length()));\n                  })\n              .sorted(Comparator.comparing(f -> f.name()))\n              .toList();\n      return Pair.of(\n          prefix,\n          new MltMetadata.ComplexField(column.physicalType(), column.logicalType(), children));\n    }\n    return Pair.of(prefix, column);\n  }\n\n  public static String createTilesetMetadataJSON(MltMetadata.TileSetMetadata pbMetadata) {\n    var root = new TreeMap<String, Object>();\n    final int version = 1;\n    root.put(\"version\", version);\n    if (pbMetadata.name != null) {\n      root.put(\"name\", pbMetadata.name);\n    }\n    if (pbMetadata.description != null) {\n      root.put(\"description\", pbMetadata.description);\n    }\n    if (pbMetadata.attribution != null) {\n      root.put(\"attribution\", pbMetadata.attribution);\n    }\n    pbMetadata.minZoom.ifPresent(integer -> root.put(\"minZoom\", integer));\n    pbMetadata.maxZoom.ifPresent(integer -> root.put(\"maxZoom\", integer));\n\n    final var bounds = new ArrayList<Map<String, Object>>();\n    if (pbMetadata.bounds.size() % 4 != 0) {\n      throw new IllegalArgumentException(\"Invalid bounds length\");\n    }\n    for (var iterator = pbMetadata.bounds.iterator(); iterator.hasNext(); ) {\n      final var bound = new TreeMap<String, Object>();\n      bound.put(\"left\", iterator.next());\n      bound.put(\"top\", iterator.next());\n      bound.put(\"right\", iterator.next());\n      bound.put(\"bottom\", iterator.next());\n      bounds.add(bound);\n    }\n    if (!bounds.isEmpty()) {\n      root.put(\"bounds\", bounds);\n    }\n\n    var centers = new ArrayList<Map<String, Object>>();\n    for (int i = 0; i < (pbMetadata.center.size() / 2); ++i) {\n      var center = new TreeMap<String, Object>();\n      center.put(\"longitude\", 2 * i);\n      center.put(\"latitude\", (2 * i) + 1);\n      centers.add(center);\n    }\n    if (!centers.isEmpty()) {\n      root.put(\"center\", centers);\n    }\n\n    return new Gson().toJson(root);\n  }\n\n  /// Write the header block for a field or column.\n  /// Takes the values individually because, despite having mostly\n  /// the same information, the field and column are separate types.\n  private static void writeColumnOrFieldType(\n      DataOutputStream stream,\n      String name,\n      boolean isNullable,\n      boolean hasLongIDs,\n      @Nullable MltMetadata.ScalarType physicalScalarType,\n      @Nullable MltMetadata.LogicalScalarType logicalScalarType,\n      @Nullable MltMetadata.ComplexType physicalComplexType,\n      @Nullable MltMetadata.LogicalComplexType logicalComplexType,\n      @Nullable SequencedCollection<MltMetadata.Field> children)\n      throws IOException {\n    // We expect exactly one of these\n    if (Stream.of(physicalScalarType, logicalScalarType, physicalComplexType, logicalComplexType)\n            .filter(Objects::nonNull)\n            .count()\n        != 1) {\n      throw new RuntimeException(\"Invalid Type\");\n    }\n\n    final boolean hasChildren = (children != null && !children.isEmpty());\n    final var typeCode =\n        MltTypeMap.Tag0x01.encodeColumnType(\n                physicalScalarType,\n                logicalScalarType,\n                physicalComplexType,\n                logicalComplexType,\n                isNullable,\n                hasChildren,\n                hasLongIDs)\n            .orElseThrow(() -> new RuntimeException(\"Unsupported Type\"));\n    EncodingUtils.putVarInt(stream, typeCode);\n\n    if (MltTypeMap.Tag0x01.columnTypeHasName(typeCode)) {\n      EncodingUtils.putString(stream, name);\n    }\n\n    if (hasChildren) {\n      EncodingUtils.putVarInt(stream, children.size());\n      for (var child : children) {\n        final boolean complex = child.type().complexType() != null;\n        final boolean logical =\n            (complex && child.type().complexType().logicalType() != null)\n                || (!complex && child.type().scalarType().logicalType() != null);\n\n        writeColumnOrFieldType(\n            stream,\n            child.name(),\n            child.type().isNullable(),\n            /* hasLongIDs= */ false,\n            (!complex && !logical) ? child.type().scalarType().physicalType() : null,\n            (!complex && logical) ? child.type().scalarType().logicalType() : null,\n            (complex && !logical) ? child.type().complexType().physicalType() : null,\n            (complex && logical) ? child.type().complexType().logicalType() : null,\n            complex ? child.type().complexType().children() : null);\n      }\n    }\n  }\n\n  /// Produce the binary tile header containing the tile metadata\n  /// @param table The metadata to be embedded in the tile\n  /// @param extent The extent of tile coordinates\n  /// @return The binary header to be embedded in the tile\n  /// @throws IOException\n  public static byte[] createEmbeddedMetadata(MltMetadata.FeatureTable table, int extent)\n      throws IOException {\n    try (var byteStream = new ByteArrayOutputStream()) {\n      try (var dataStream = new DataOutputStream(byteStream)) {\n        EncodingUtils.putString(dataStream, table.name());\n        EncodingUtils.putVarInt(dataStream, extent);\n        EncodingUtils.putVarInt(dataStream, table.columns().size());\n        for (var column : table.columns()) {\n          if (column.columnScope() != MltMetadata.ColumnScope.FEATURE) {\n            throw new RuntimeException(\"Vertex scoped properties are not yet supported\");\n          }\n          writeColumnOrFieldType(\n              dataStream,\n              column.getName(),\n              column.isNullable(),\n              column.isScalar() && column.field().type().scalarType().hasLongId(),\n              column.getScalarType().orElse(null),\n              column.getLogicalScalarType().orElse(null),\n              column.getComplexType().orElse(null),\n              column.getLogicalComplexType().orElse(null),\n              column.getChildren().orElse(null));\n        }\n      }\n      return byteStream.toByteArray();\n    }\n  }\n\n  /*\n   * Converts a collection of layers into an MLT tile\n   *\n   * @param sourceLayers The input layers\n   * @param tilesetMetadata Metadata of the tile\n   * @param config Settings for the conversion\n   * @param tessellateSource Optional URI of a tessellation service to use if polygon pre-tessellation is enabled\n   * @return Converted MapLibreTile as a byte array\n   * @throws IOException\n   */\n  public static byte[] encode(\n      LayerSource sourceLayers,\n      MltMetadata.TileSetMetadata tilesetMetadata,\n      ConversionConfig config,\n      @Nullable URI tessellateSource)\n      throws IOException {\n    return encode(\n            sourceLayers, tilesetMetadata, config, tessellateSource, ByteArrayOutputStream::new)\n        .toByteArray();\n  }\n\n  /*\n   * Converts a collection of layers into an MLT tile\n   *\n   * @param sourceLayers The input layers\n   * @param tilesetMetadata Metadata of the tile\n   * @param config Settings for the conversion\n   * @param tessellateSource Optional URI of a tessellation service to use if polygon pre-tessellation is enabled\n   * @param outputStreamSupplier A function producing the output stream to which the converted tile will be written.\n   * @return The output stream to which the data was written\n   * @throws IOException\n   */\n  public static <T extends OutputStream> T encode(\n      @NotNull LayerSource sourceLayers,\n      @NotNull MltMetadata.TileSetMetadata tilesetMetadata,\n      @NotNull ConversionConfig config,\n      @Nullable URI tessellateSource,\n      @NotNull Function<Integer, T> outputStreamSupplier)\n      throws IOException {\n    // Convert the list of metadatas (one per layer) into a lookup by the first and only layer name\n    // We assume that the names are unique.\n    final var metaMap =\n        tilesetMetadata.featureTables.stream()\n            .collect(\n                Collectors.toMap(\n                    t -> t.name(),\n                    table -> table,\n                    (existing, replacement) -> {\n                      throw new RuntimeException(\"duplicate key\");\n                    }));\n\n    final var physicalLevelTechnique =\n        config.useFastPFOR() ? PhysicalLevelTechnique.FAST_PFOR : PhysicalLevelTechnique.VARINT;\n\n    final var tileBuffers = new ArrayList<byte[]>((int) sourceLayers.getLayerCount() * 10);\n    for (var sourceLayer : sourceLayers.getLayers()) {\n      final var featureTableName = sourceLayer.name();\n\n      if (config.layerFilterPattern() != null) {\n        final var matcher = config.layerFilterPattern().matcher(featureTableName);\n        final var isMatch = matcher.matches() ^ config.layerFilterInvert();\n        if (!isMatch) {\n          continue;\n        }\n      }\n\n      final var layerMetadata = metaMap.get(featureTableName);\n      if (layerMetadata == null) {\n        throw new RuntimeException(\"Missing Metadata\");\n      }\n\n      final var sourceFeatures = sourceLayer.features();\n      if (sourceFeatures.isEmpty()) {\n        continue;\n      }\n\n      final var optimizations = Optional.ofNullable(config.optimizations());\n      final var featureTableOptimizations = optimizations.map(opt -> opt.get(featureTableName));\n\n      final var createPolygonOutline =\n          config.outlineFeatureTableNames().contains(featureTableName)\n              || config.outlineFeatureTableNames().contains(\"ALL\");\n      final var result =\n          sortFeaturesAndEncodeGeometryColumn(\n              config,\n              featureTableOptimizations,\n              sourceFeatures,\n              sourceFeatures,\n              physicalLevelTechnique,\n              createPolygonOutline,\n              tessellateSource);\n      final var sortedFeatures = result.getLeft();\n      final var encodedGeometryColumn = result.getRight();\n      final var encodedGeometryFieldMetadata =\n          EncodingUtils.encodeVarint(encodedGeometryColumn.numStreams(), false);\n\n      final var encodedPropertyColumns =\n          encodePropertyColumns(config, layerMetadata, sortedFeatures, featureTableOptimizations);\n\n      final var featureTableBodyBuffer = new ArrayList<byte[]>(20);\n      if (config.includeIds()) {\n        final var idMetadata =\n            layerMetadata.columns().stream()\n                .filter(MltTypeMap.Tag0x01::isID)\n                .findFirst()\n                .orElseThrow();\n\n        // Write ID as a 32- or 64-bit scalar depending on the flag stored in the column metadata.\n        // The decoding assumes unsigned (no zigzag)\n        final var rawType =\n            idMetadata.field().type().scalarType().hasLongId()\n                ? MltMetadata.ScalarType.UINT_64\n                : MltMetadata.ScalarType.UINT_32;\n        final var scalarColumnMetadata =\n            new MltMetadata.Column(MltMetadata.scalarFieldType(rawType, idMetadata.isNullable()));\n        featureTableBodyBuffer.addAll(\n            PropertyEncoder.encodeScalarPropertyColumn(\n                scalarColumnMetadata,\n                true,\n                sortedFeatures,\n                physicalLevelTechnique,\n                config.useFSST(),\n                config.typeMismatchPolicy() == ConversionConfig.TypeMismatchPolicy.COERCE,\n                config.integerEncodingOption()));\n      }\n\n      featureTableBodyBuffer.add(encodedGeometryFieldMetadata);\n      featureTableBodyBuffer.addAll(encodedGeometryColumn.encodedValues());\n      featureTableBodyBuffer.addAll(encodedPropertyColumns);\n\n      final var metadataBuffer = createEmbeddedMetadata(layerMetadata, sourceLayer.tileExtent());\n\n      final var tag = 1;\n      final var tagBuffer = EncodingUtils.encodeVarint(tag, false);\n      final var tagLength =\n          tagBuffer.length\n              + metadataBuffer.length\n              + ByteArrayUtil.totalLength(featureTableBodyBuffer);\n\n      tileBuffers.add(EncodingUtils.encodeVarint(tagLength, false));\n      tileBuffers.add(tagBuffer);\n      tileBuffers.add(metadataBuffer);\n      tileBuffers.addAll(featureTableBodyBuffer);\n    }\n\n    final var targetStream = outputStreamSupplier.apply(ByteArrayUtil.totalLength(tileBuffers));\n    Objects.requireNonNull(targetStream, \"Output stream supplier returned null\");\n    return ByteArrayUtil.concat(targetStream, tileBuffers);\n  }\n\n  @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n  private static ArrayList<byte[]> encodePropertyColumns(\n      ConversionConfig config,\n      MltMetadata.FeatureTable featureTableMetadata,\n      SequencedCollection<Feature> sortedFeatures,\n      @NotNull Optional<FeatureTableOptimizations> featureTableOptimizations)\n      throws IOException {\n    final var propertyColumns = filterPropertyColumns(featureTableMetadata);\n    @NotNull\n    final var columnMappings =\n        featureTableOptimizations.map(FeatureTableOptimizations::columnMappings).orElse(List.of());\n    return PropertyEncoder.encodePropertyColumns(\n        propertyColumns,\n        sortedFeatures,\n        config.useFastPFOR(),\n        config.useFSST(),\n        config.typeMismatchPolicy() == ConversionConfig.TypeMismatchPolicy.COERCE,\n        columnMappings,\n        config.integerEncodingOption());\n  }\n\n  @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n  private static Pair<SequencedCollection<Feature>, GeometryEncoder.EncodedGeometryColumn>\n      sortFeaturesAndEncodeGeometryColumn(\n          ConversionConfig config,\n          Optional<FeatureTableOptimizations> featureTableOptimizations,\n          SequencedCollection<Feature> sortedFeatures,\n          SequencedCollection<Feature> sourceFeatures,\n          PhysicalLevelTechnique physicalLevelTechnique,\n          boolean encodePolygonOutlines,\n          @Nullable URI tessellateSource)\n          throws IOException {\n    /*\n     * Following simple strategy is currently used for ordering the features when sorting is enabled:\n     * - if id column is present and ids should not be reassigned -> sort id column\n     * - if id column is presented and ids can be reassigned  -> sort geometry column (VertexOffsets)\n     *   and regenerate ids\n     * - if id column is not presented -> sort geometry column\n     * In general finding an optimal column arrangement is NP-hard, but by implementing a more sophisticated strategy\n     * based on the latest academic results in the future, the compression ratio can be further improved\n     * */\n\n    if (sourceFeatures.isEmpty()) {\n      throw new IllegalArgumentException(\"No features to encode\");\n    }\n\n    final var isColumnSortable =\n        config.includeIds()\n            && featureTableOptimizations.map(FeatureTableOptimizations::allowSorting).orElse(false);\n    final var allowIdRegeneration =\n        featureTableOptimizations.map(FeatureTableOptimizations::allowIdRegeneration).orElse(false);\n    if (isColumnSortable && !allowIdRegeneration) {\n      sortedFeatures = sortFeaturesById(sourceFeatures).toList();\n    }\n\n    var ids = sortedFeatures.stream().map(Feature::idOrNull).collect(Collectors.toList());\n    var geometries = sortedFeatures.stream().map(Feature::getGeometry).collect(Collectors.toList());\n\n    if (geometries.isEmpty()) {\n      throw new IllegalArgumentException(\"No geometries to encode\");\n    }\n\n    /* Only sort geometries if ids can be reassigned since sorting the id column turned out\n     * to be more efficient in the tests */\n    final var sortSettings =\n        new GeometryEncoder.SortSettings(isColumnSortable && allowIdRegeneration, ids);\n    /* Morton Vertex Dictionary encoding is currently not supported in pre-tessellation */\n    final var useMortonEncoding = config.useMortonEncoding() && !config.preTessellatePolygons();\n    final var geometryEncodingOption = config.geometryEncodingOption();\n    final var encodedGeometryColumn =\n        GeometryEncoder.encodeGeometryColumn(\n            geometries,\n            physicalLevelTechnique,\n            sortSettings,\n            useMortonEncoding,\n            config.preTessellatePolygons(),\n            encodePolygonOutlines,\n            tessellateSource,\n            geometryEncodingOption);\n\n    if (encodedGeometryColumn.geometryColumnSorted()) {\n      sortedFeatures =\n          ids.stream()\n              .map(\n                  id ->\n                      sourceFeatures.stream()\n                          .filter(fe -> Objects.equals(fe.idOrNull(), id))\n                          .findFirst()\n                          .orElseThrow())\n              .collect(Collectors.toList());\n    }\n\n    if (config.includeIds() && allowIdRegeneration) {\n      sortedFeatures = generateSequenceIds(sortedFeatures).toList();\n    }\n\n    return Pair.of(sortedFeatures, encodedGeometryColumn);\n  }\n\n  private static List<MltMetadata.Column> filterPropertyColumns(\n      MltMetadata.FeatureTable featureTableMetadata) {\n    return featureTableMetadata.columns().stream()\n        .filter(f -> !MltTypeMap.Tag0x01.isID(f) && !MltTypeMap.Tag0x01.isGeometry(f))\n        .collect(Collectors.toList());\n  }\n\n  private static Stream<Feature> sortFeaturesById(Collection<Feature> features) {\n    return features.stream()\n        .sorted(Comparator.comparing(Feature::hasId).thenComparingLong(Feature::getId));\n  }\n\n  private static Stream<Feature> generateSequenceIds(Collection<Feature> features) {\n    final var idCounter = new long[] {0};\n    return features.stream()\n        .map(feature -> feature.toBuilder().id(idCounter[0]++).index(feature.getIndex()).build());\n  }\n\n  private static MltMetadata.Column createScalarColumnScheme(\n      String columnName,\n      @SuppressWarnings(\"SameParameterValue\") boolean nullable,\n      MltMetadata.ScalarType type) {\n    return new MltMetadata.Column(\n        new MltMetadata.Field(MltMetadata.scalarFieldType(type, nullable), columnName));\n  }\n\n  private static MltMetadata.Field createScalarFieldScheme(\n      String fieldName,\n      @SuppressWarnings(\"SameParameterValue\") boolean nullable,\n      MltMetadata.ScalarType type) {\n    return new MltMetadata.Field(MltMetadata.scalarFieldType(type, nullable), fieldName);\n  }\n\n  private static MltMetadata.Column createComplexColumnScheme(\n      @SuppressWarnings(\"SameParameterValue\") @Nullable String columnName,\n      @SuppressWarnings(\"SameParameterValue\") boolean nullable,\n      @SuppressWarnings(\"SameParameterValue\") MltMetadata.ComplexType type) {\n    return new MltMetadata.Column(\n        new MltMetadata.Field(MltMetadata.complexFieldType(type, nullable), columnName));\n  }\n\n  private static MltMetadata.ComplexField createComplexColumn() {\n    return new MltMetadata.ComplexField(MltMetadata.ComplexType.STRUCT);\n  }\n\n  private static MltMetadata.Column createColumn(\n      String columnName, MltMetadata.ComplexField complexField) {\n    final var isNullable = false; // See `PropertyDecoder.decodePropertyColumn()`\n    return new MltMetadata.Column(\n        new MltMetadata.Field(new MltMetadata.FieldType(complexField, isNullable), columnName));\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/MortonSettings.java",
    "content": "package org.maplibre.mlt.converter;\n\npublic final class MortonSettings {\n  public int numBits;\n  public int coordinateShift;\n\n  public MortonSettings(int numBits, int coordinateShift) {\n    this.numBits = numBits;\n    this.coordinateShift = coordinateShift;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/BooleanEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class BooleanEncoder {\n\n  private BooleanEncoder() {}\n\n  /*\n   * Combines a BitVector encoding with the Byte RLE encoding form the ORC format\n   * */\n  public static ArrayList<byte[]> encodeBooleanStream(\n      Boolean[] values, PhysicalStreamType streamType) throws IOException {\n    final var valueStream = new BitSet(values.length);\n    for (var i = 0; i < values.length; i++) {\n      valueStream.set(i, values[i]);\n    }\n\n    final var encodedValueStream = EncodingUtils.encodeBooleanRle(valueStream, values.length);\n    /* For Boolean RLE the additional information provided by the RleStreamMetadata class are not needed */\n    final var result =\n        new StreamMetadata(\n                streamType,\n                null,\n                LogicalLevelTechnique.RLE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                values.length,\n                encodedValueStream.length)\n            .encode();\n\n    result.add(encodedValueStream);\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/ByteRleEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\n\n/**\n * Encodes byte data using run-length encoding.\n *\n * <p>The encoding format uses a control byte followed by data:\n *\n * <ul>\n *   <li>Control byte 0x00-0x7F: Run of (control + 3) copies of the next byte\n *   <li>Control byte 0x80-0xFF: (256 - control) literal bytes follow\n * </ul>\n */\npublic class ByteRleEncoder {\n  static final int MIN_REPEAT_SIZE = 3;\n  static final int MAX_LITERAL_SIZE = 128;\n  static final int MAX_REPEAT_SIZE = 127 + MIN_REPEAT_SIZE;\n\n  private final ByteArrayOutputStream output;\n  private final byte[] literals = new byte[MAX_LITERAL_SIZE];\n  private int numLiterals = 0;\n  private boolean repeat = false;\n  private int tailRunLength = 0;\n\n  public ByteRleEncoder() {\n    this.output = new ByteArrayOutputStream();\n  }\n\n  private void writeValues() throws IOException {\n    if (numLiterals != 0) {\n      if (repeat) {\n        output.write(numLiterals - MIN_REPEAT_SIZE);\n        output.write(literals[0] & 0xFF);\n      } else {\n        output.write(-numLiterals);\n        output.write(literals, 0, numLiterals);\n      }\n      repeat = false;\n      tailRunLength = 0;\n      numLiterals = 0;\n    }\n  }\n\n  public void write(byte value) throws IOException {\n    if (numLiterals == 0) {\n      literals[numLiterals++] = value;\n      tailRunLength = 1;\n    } else if (repeat) {\n      if (value == literals[0]) {\n        numLiterals++;\n        if (numLiterals == MAX_REPEAT_SIZE) {\n          writeValues();\n        }\n      } else {\n        writeValues();\n        literals[numLiterals++] = value;\n        tailRunLength = 1;\n      }\n    } else {\n      if (value == literals[numLiterals - 1]) {\n        tailRunLength++;\n      } else {\n        tailRunLength = 1;\n      }\n      if (tailRunLength == MIN_REPEAT_SIZE) {\n        if (numLiterals + 1 == MIN_REPEAT_SIZE) {\n          repeat = true;\n          numLiterals++;\n        } else {\n          numLiterals -= MIN_REPEAT_SIZE - 1;\n          writeValues();\n          literals[0] = value;\n          repeat = true;\n          numLiterals = MIN_REPEAT_SIZE;\n        }\n      } else {\n        literals[numLiterals++] = value;\n        if (numLiterals == MAX_LITERAL_SIZE) {\n          writeValues();\n        }\n      }\n    }\n  }\n\n  public void flush() throws IOException {\n    writeValues();\n  }\n\n  public byte[] toByteArray() throws IOException {\n    flush();\n    return output.toByteArray();\n  }\n\n  /** Convenience method to encode a byte array. */\n  public static byte[] encode(byte[] values) throws IOException {\n    ByteRleEncoder encoder = new ByteRleEncoder();\n    for (byte value : values) {\n      encoder.write(value);\n    }\n    return encoder.toByteArray();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/DoubleEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class DoubleEncoder {\n\n  private DoubleEncoder() {}\n\n  public static ArrayList<byte[]> encodeDoubleStream(List<Double> values) throws IOException {\n    // TODO: add encodings -> RLE, Dictionary, PDE\n    final double[] doubleArray = new double[values.size()];\n    for (int i = 0; i < values.size(); i++) {\n      doubleArray[i] = values.get(i);\n    }\n    final var encodedValueStream = EncodingUtils.encodeDoublesLE(doubleArray);\n\n    final var result =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                null,\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                values.size(),\n                encodedValueStream.length)\n            .encode();\n\n    result.add(encodedValueStream);\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/EncodingUtils.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.Collection;\nimport java.util.zip.GZIPInputStream;\nimport java.util.zip.GZIPOutputStream;\nimport me.lemire.integercompression.Composition;\nimport me.lemire.integercompression.FastPFOR;\nimport me.lemire.integercompression.IntWrapper;\nimport me.lemire.integercompression.IntegerCODEC;\nimport me.lemire.integercompression.VariableByte;\nimport org.apache.commons.lang3.ArrayUtils;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.maplibre.mlt.converter.CollectionUtils;\n\npublic class EncodingUtils {\n\n  // https://github.com/bazelbuild/bazel/blob/6ce603d8/src/main/java/com/google/devtools/build/lib/util/VarInt.java\n  /** Maximum encoded size of 32-bit positive integers (in bytes) */\n  public static final int MAX_VARINT_SIZE = 5;\n\n  /** maximum encoded size of 64-bit longs, and negative 32-bit ints (in bytes) */\n  public static final int MAX_VARLONG_SIZE = 10;\n\n  public static byte[] gzip(byte[] buffer) throws IOException {\n    ByteArrayOutputStream baos = new ByteArrayOutputStream();\n    GZIPOutputStream gzipOut = new GZIPOutputStream(baos);\n    gzipOut.write(buffer);\n    gzipOut.close();\n    baos.close();\n\n    return baos.toByteArray();\n  }\n\n  public static byte[] unzip(byte[] buffer) throws IOException {\n    try (var inputStream = new ByteArrayInputStream(buffer)) {\n      try (var gZIPInputStream = new GZIPInputStream(inputStream)) {\n        return gZIPInputStream.readAllBytes();\n      }\n    }\n  }\n\n  /** Convert the floats to IEEE754 floating point numbers in Little Endian byte order. */\n  public static byte[] encodeFloatsLE(float[] values) {\n    var buffer = ByteBuffer.allocate(values.length * 4).order(ByteOrder.LITTLE_ENDIAN);\n    for (var value : values) {\n      buffer.putFloat(value);\n    }\n    return buffer.array();\n  }\n\n  /** Convert the doubles to IEEE754 floating point numbers in Little Endian byte order. */\n  public static byte[] encodeDoublesLE(double[] values) {\n    var buffer = ByteBuffer.allocate(values.length * 8).order(ByteOrder.LITTLE_ENDIAN);\n    for (var value : values) {\n      buffer.putDouble(value);\n    }\n    return buffer.array();\n  }\n\n  // Source:\n  // https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/util/VarInt.java\n  public static byte[] encodeVarints(int[] values, boolean zigZagEncode, boolean deltaEncode)\n      throws IOException {\n    var encodedValues = values;\n    if (deltaEncode) {\n      encodedValues = encodeDeltas(values);\n    }\n\n    if (zigZagEncode) {\n      encodedValues = encodeZigZag(encodedValues);\n    }\n\n    var varintBuffer =\n        ByteBuffer.wrap(\n            new byte[Arrays.stream(encodedValues).map(EncodingUtils::getVarIntSize).sum()]);\n    for (var value : encodedValues) {\n      putVarInt(value, varintBuffer);\n    }\n    return varintBuffer.array();\n  }\n\n  public static byte[] encodeVarints(long[] values, boolean zigZagEncode, boolean deltaEncode)\n      throws IOException {\n    var encodedValues = values;\n    if (deltaEncode) {\n      encodedValues = encodeDeltas(values);\n    }\n\n    if (zigZagEncode) {\n      encodedValues = encodeZigZag(encodedValues);\n    }\n\n    var varintBuffer =\n        ByteBuffer.wrap(\n            new byte[Arrays.stream(encodedValues).mapToInt(EncodingUtils::getVarLongSize).sum()]);\n    for (var value : encodedValues) {\n      putVarInt(value, varintBuffer);\n    }\n    return varintBuffer.array();\n  }\n\n  public static byte[] encodeVarints(\n      Collection<Integer> values, boolean zigZagEncode, boolean deltaEncode) throws IOException {\n    return encodeVarints(CollectionUtils.unboxInts(values), zigZagEncode, deltaEncode);\n  }\n\n  public static byte[] encodeLongVarints(long[] values, boolean zigZagEncode, boolean deltaEncode)\n      throws IOException {\n    return encodeVarints(values, zigZagEncode, deltaEncode);\n  }\n\n  public static byte[] encodeVarint(int value, boolean zigZagEncode) throws IOException {\n    if (zigZagEncode) {\n      value = encodeZigZag(value);\n    }\n    final var buffer = ByteBuffer.wrap(new byte[getVarIntSize(value)]);\n    return putVarInt(value, buffer).array();\n  }\n\n  // Source:\n  // https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/util/VarInt.java\n  /**\n   * Encodes an integer in a variable-length encoding, 7 bits per byte, into a destination byte[],\n   * following the protocol buffer convention.\n   *\n   * @param v the int value to write to sink\n   * @param sink the sink buffer to write to\n   */\n  public static ByteBuffer putVarInt(int v, ByteBuffer sink) throws IOException {\n    do {\n      // Encode next 7 bits + terminator bit\n      final int bits = v & 0x7F;\n      v >>>= 7;\n      sink.put((byte) (bits + ((v != 0) ? 0x80 : 0)));\n    } while (v != 0);\n    return sink;\n  }\n\n  static ByteBuffer putVarInt(long v, ByteBuffer sink) throws IOException {\n    do {\n      // Encode next 7 bits + terminator bit\n      final long bits = v & 0x7F;\n      v >>>= 7;\n      sink.put((byte) (bits + ((v != 0) ? 0x80 : 0)));\n    } while (v != 0);\n    return sink;\n  }\n\n  @SuppressWarnings(\"UnusedReturnValue\")\n  public static DataOutputStream putVarInt(DataOutputStream stream, int v) throws IOException {\n    final var buffer = ByteBuffer.wrap(new byte[MAX_VARINT_SIZE]);\n    putVarInt(v, buffer);\n    stream.write(buffer.array(), 0, buffer.position());\n    return stream;\n  }\n\n  private static final int DATA_BITS_PER_ENCODED_BYTE = 7;\n\n  public static int getVarIntSize(int value) {\n    final var bitsNeeded = Integer.SIZE - Integer.numberOfLeadingZeros(value);\n    return Math.max(1, (bitsNeeded + DATA_BITS_PER_ENCODED_BYTE - 1) / DATA_BITS_PER_ENCODED_BYTE);\n  }\n\n  public static int getVarLongSize(long value) {\n    final var bitsNeeded = Long.SIZE - Long.numberOfLeadingZeros(value);\n    return Math.max(1, (bitsNeeded + DATA_BITS_PER_ENCODED_BYTE - 1) / DATA_BITS_PER_ENCODED_BYTE);\n  }\n\n  @SuppressWarnings(\"UnusedReturnValue\")\n  public static DataOutputStream putString(DataOutputStream stream, String s) throws IOException {\n    final var bytes = s.getBytes(StandardCharsets.UTF_8);\n    putVarInt(stream, bytes.length);\n    stream.write(bytes);\n    return stream;\n  }\n\n  public static long[] encodeZigZag(long[] values) {\n    long[] result = new long[values.length];\n    for (int i = 0; i < values.length; i++) {\n      result[i] = encodeZigZag(values[i]);\n    }\n    return result;\n  }\n\n  public static int[] encodeZigZag(int[] values) {\n    int[] result = new int[values.length];\n    for (int i = 0; i < values.length; i++) {\n      result[i] = encodeZigZag(values[i]);\n    }\n    return result;\n  }\n\n  public static long encodeZigZag(long value) {\n    return (value << 1) ^ (value >> 63);\n  }\n\n  public static int encodeZigZag(int value) {\n    return (value >> 31) ^ (value << 1);\n  }\n\n  public static long[] encodeDeltas(long[] values) {\n    var deltaValues = new long[values.length];\n    var previousValue = 0L;\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      deltaValues[i] = value - previousValue;\n      previousValue = value;\n    }\n    return deltaValues;\n  }\n\n  public static int[] encodeDeltas(int[] values) {\n    var deltaValues = new int[values.length];\n    var previousValue = 0;\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      deltaValues[i] = value - previousValue;\n      previousValue = value;\n    }\n    return deltaValues;\n  }\n\n  /**\n   * @return Pair of runs and values.\n   */\n  public static Pair<int[], int[]> encodeRle(int[] values) {\n    var valueBuffer = new ArrayList<Integer>();\n    var runsBuffer = new ArrayList<Integer>();\n    var previousValue = 0;\n    var runs = 0;\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      if (previousValue != value && i != 0) {\n        valueBuffer.add(previousValue);\n        runsBuffer.add(runs);\n        runs = 0;\n      }\n\n      runs++;\n      previousValue = value;\n    }\n\n    valueBuffer.add(values[values.length - 1]);\n    runsBuffer.add(runs);\n\n    return Pair.of(CollectionUtils.unboxInts(runsBuffer), CollectionUtils.unboxInts(valueBuffer));\n  }\n\n  /**\n   * @return Pair of runs and values.\n   */\n  // TODO: merge this method with the int variant\n  public static Pair<long[], long[]> encodeRle(long[] values) {\n    var valueBuffer = new ArrayList<Long>();\n    var runsBuffer = new ArrayList<Integer>();\n    var previousValue = 0L;\n    var runs = 0;\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      if (previousValue != value && i != 0) {\n        valueBuffer.add(previousValue);\n        runsBuffer.add(runs);\n        runs = 0;\n      }\n\n      runs++;\n      previousValue = value;\n    }\n\n    valueBuffer.add(values[values.length - 1]);\n    runsBuffer.add(runs);\n    return Pair.of(CollectionUtils.unboxLongs(runsBuffer), CollectionUtils.unboxLongs(valueBuffer));\n  }\n\n  public static byte[] encodeFastPfor128(int[] values, boolean zigZagEncode, boolean deltaEncode) {\n    /*\n     * Note that this does not use differential coding: if you are working on sorted lists,\n     * you should first compute deltas, @see me.lemire.integercompression.differential.Delta#delta\n     * */\n    var encodedValues = values;\n    if (deltaEncode) {\n      encodedValues = encodeDeltas(values);\n    }\n\n    if (zigZagEncode) {\n      encodedValues = encodeZigZag(encodedValues);\n    }\n\n    IntegerCODEC ic = new Composition(new FastPFOR(), new VariableByte());\n    IntWrapper inputoffset = new IntWrapper(0);\n    IntWrapper outputoffset = new IntWrapper(0);\n    final int[] compressed = new int[encodedValues.length + 1024];\n    ic.compress(encodedValues, inputoffset, encodedValues.length, compressed, outputoffset);\n    final var totalSize = outputoffset.intValue() * 4;\n\n    final var compressedBuffer = new byte[totalSize];\n    var valueCounter = 0;\n    for (var i = 0; i < totalSize; i += 4) {\n      var value = compressed[valueCounter++];\n      compressedBuffer[i] = (byte) (value >>> 24);\n      compressedBuffer[i + 1] = (byte) (value >>> 16);\n      compressedBuffer[i + 2] = (byte) (value >>> 8);\n      compressedBuffer[i + 3] = (byte) value;\n    }\n\n    return compressedBuffer;\n  }\n\n  public static byte[] encodeByteRle(byte[] values) throws IOException {\n    return ByteRleEncoder.encode(values);\n  }\n\n  public static byte[] encodeBooleanRle(BitSet bitSet, int numValues) throws IOException {\n    var presentStream = bitSet.toByteArray();\n    /* The BitSet only returns the bytes until the last set bit */\n    var numMissingBytes = (int) Math.ceil(numValues / 8d) - (int) Math.ceil(bitSet.length() / 8d);\n    if (numMissingBytes != 0) {\n      var paddingBytes = new byte[numMissingBytes];\n      Arrays.fill(paddingBytes, (byte) 0);\n      presentStream = ArrayUtils.addAll(presentStream, paddingBytes);\n    }\n\n    return EncodingUtils.encodeByteRle(presentStream);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/FloatEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class FloatEncoder {\n\n  private FloatEncoder() {}\n\n  public static ArrayList<byte[]> encodeFloatStream(List<Float> values) throws IOException {\n    // TODO: add encodings -> RLE, Dictionary, PDE\n    final float[] floatArray = new float[values.size()];\n    for (int i = 0; i < values.size(); i++) {\n      floatArray[i] = values.get(i);\n    }\n    final var encodedValueStream = EncodingUtils.encodeFloatsLE(floatArray);\n\n    final var valuesMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                null,\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                values.size(),\n                encodedValueStream.length)\n            .encode();\n\n    valuesMetadata.add(encodedValueStream);\n    return valuesMetadata;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/GeometryEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport static org.maplibre.mlt.converter.encodings.IntegerEncoder.encodeFastPfor;\nimport static org.maplibre.mlt.converter.encodings.IntegerEncoder.encodeVarint;\n\nimport jakarta.annotation.Nullable;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.jetbrains.annotations.NotNull;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.locationtech.jts.geom.MultiPoint;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption;\nimport org.maplibre.mlt.converter.geometry.GeometryType;\nimport org.maplibre.mlt.converter.geometry.GeometryUtils;\nimport org.maplibre.mlt.converter.geometry.HilbertCurve;\nimport org.maplibre.mlt.converter.geometry.SpaceFillingCurve;\nimport org.maplibre.mlt.converter.geometry.Vertex;\nimport org.maplibre.mlt.converter.geometry.ZOrderCurve;\nimport org.maplibre.mlt.converter.tessellation.TessellationUtils;\nimport org.maplibre.mlt.metadata.stream.DictionaryType;\nimport org.maplibre.mlt.metadata.stream.LengthType;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.LogicalStreamType;\nimport org.maplibre.mlt.metadata.stream.OffsetType;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\nimport org.maplibre.mlt.util.ExceptionUtil;\nimport org.maplibre.mlt.util.OptionalUtil;\n\npublic class GeometryEncoder {\n\n  public record EncodedGeometryColumn(\n      int numStreams,\n      ArrayList<byte[]> encodedValues,\n      int maxVertexValue,\n      boolean geometryColumnSorted) {}\n\n  public record SortSettings(boolean isSortable, List<Long> featureIds) {}\n\n  private GeometryEncoder() {}\n\n  public static EncodedGeometryColumn encodeGeometryColumn(\n      List<Geometry> geometries,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      SortSettings sortSettings,\n      boolean enableMortonEncoding,\n      boolean enableTessellation,\n      boolean encodePolygonOutlines,\n      @Nullable URI tessellateSource,\n      @NotNull IntegerEncodingOption encodingOption)\n      throws IOException {\n    final var geometryTypes = new ArrayList<Integer>();\n    final var numGeometries = new ArrayList<Integer>();\n    final var numParts = new ArrayList<Integer>();\n    final var numRings = new ArrayList<Integer>();\n    final var numTriangles = enableTessellation ? new ArrayList<Integer>() : null;\n    final var indexBuffer = enableTessellation ? new ArrayList<Integer>() : null;\n    final var vertexBuffer = new ArrayList<Vertex>();\n    prepareGeometry(\n        geometries,\n        numGeometries,\n        geometryTypes,\n        vertexBuffer,\n        numParts,\n        numRings,\n        numTriangles,\n        indexBuffer,\n        tessellateSource);\n\n    if (vertexBuffer.isEmpty()) {\n      throw new IllegalArgumentException(\"The geometry column contains no vertices\");\n    }\n\n    /* Test if Plain, Vertex Dictionary or Morton Encoded Vertex Dictionary is the most efficient\n     * -> Plain -> convert VertexBuffer with Delta Encoding and specified Physical Level Technique\n     * -> Dictionary -> convert VertexOffsets with IntegerEncoder and VertexBuffer with Delta Encoding and specified Physical Level Technique\n     * -> Morton Encoded Dictionary -> convert VertexOffsets with Integer Encoder and VertexBuffer with IntegerEncoder\n     * */\n\n    final var vertexLimits = getVertexLimits(vertexBuffer);\n    final var tryHilbertEncoding =\n        HilbertCurve.isRangeSupported(vertexLimits.min, vertexLimits.max);\n    final var tryMortonEncoding =\n        enableMortonEncoding && ZOrderCurve.isRangeSupported(vertexLimits.min, vertexLimits.max);\n    final Optional<HilbertCurve> hilbertCurve =\n        tryHilbertEncoding\n            ? Optional.of(new HilbertCurve(vertexLimits.min, vertexLimits.max))\n            : Optional.empty();\n    final Optional<ZOrderCurve> zOrderCurve =\n        tryMortonEncoding\n            ? Optional.of(new ZOrderCurve(vertexLimits.min, vertexLimits.max))\n            : Optional.empty();\n\n    // TODO: if the ratio is lower than 2 dictionary encoding has not to be considered?\n\n    final var vertexDictionary = hilbertCurve.map(c -> addVerticesToDictionary(vertexBuffer, c));\n    final var vertexDictionaryHilbertIndexes = vertexDictionary.map(Pair::getLeft);\n    final var vertexDictionaryHilbertMap =\n        vertexDictionaryHilbertIndexes.map(GeometryEncoder::reverseMap);\n    final var vertexDictionaryVertices = vertexDictionary.map(Pair::getRight);\n    final var vertexDictionaryOffsets =\n        hilbertCurve.flatMap(\n            c -> vertexDictionaryHilbertMap.map(m -> getVertexOffsets(vertexBuffer, m::get, c)));\n    final var zigZagDeltaVertexDictionary =\n        vertexDictionaryVertices.map(GeometryEncoder::zigZagDeltaEncodeVertices);\n    final var mortonEncodedDictionary =\n        zOrderCurve.map(c -> addVerticesToMortonDictionary(vertexBuffer, c));\n    final var mortonEncodedDictionaryOffsets =\n        zOrderCurve.flatMap(\n            c ->\n                mortonEncodedDictionary.map(\n                    d -> getVertexOffsets(vertexBuffer, reverseMap(d)::get, c)));\n\n    // TODO: refactor this simple approach to also work with mixed geometries\n    var geometryColumnSorted = false;\n    if (sortSettings.isSortable && numGeometries.isEmpty() && numRings.isEmpty()) {\n      if (numParts.size() == sortSettings.featureIds.size()) {\n        /* Currently the VertexOffsets are only sorted if all geometries in the geometry column are of type LineString */\n        if (mortonEncodedDictionaryOffsets.isPresent()) {\n          GeometryUtils.sortVertexOffsets(\n              numParts, mortonEncodedDictionaryOffsets.get(), sortSettings.featureIds());\n          geometryColumnSorted = true;\n        }\n      } else if (numParts.isEmpty() && hilbertCurve.isPresent()) {\n        GeometryUtils.sortPoints(vertexBuffer, hilbertCurve.get(), sortSettings.featureIds);\n        geometryColumnSorted = true;\n      }\n    }\n\n    final var zigZagDeltaVertexBuffer = zigZagDeltaEncodeVertices(vertexBuffer);\n    final var encodedVertexBufferStream =\n        encodeVertexBuffer(zigZagDeltaVertexBuffer, physicalLevelTechnique);\n\n    // TODO: All of these are done only to determine which encoding to use, the actual result is\n    //       discarded!  Additionally, it's only the size of the raw data, not including metadata.\n    //       Instead, we should select based on the size of what's actually written.\n    final var plainVertexBufferSize =\n        IntegerEncoder.encodeInt(\n                zigZagDeltaVertexBuffer, physicalLevelTechnique, false, encodingOption)\n            .encodedValues\n            .length;\n    final var encodedMortonEncodedDictionaryOffsetsSize =\n        mortonEncodedDictionaryOffsets.map(\n            o ->\n                IntegerEncoder.encodeInt(o, physicalLevelTechnique, false, encodingOption)\n                    .encodedValues\n                    .length);\n    final var encodedDictionaryOffsetsSize =\n        vertexDictionaryOffsets.map(\n            o ->\n                IntegerEncoder.encodeInt(o, physicalLevelTechnique, false, encodingOption)\n                    .encodedValues\n                    .length);\n    final var encodedVertexDictionarySize =\n        zigZagDeltaVertexDictionary.map(\n            d ->\n                IntegerEncoder.encodeInt(d, physicalLevelTechnique, false, encodingOption)\n                    .encodedValues\n                    .length);\n    final var encodedMortonVertexDictionarySize =\n        mortonEncodedDictionary.map(\n            ExceptionUtil.unchecked(\n                d ->\n                    IntegerEncoder.encodeMortonCodes(d, physicalLevelTechnique)\n                        .encodedValues\n                        .length));\n    final var dictionaryEncodedSize =\n        encodedDictionaryOffsetsSize.flatMap(a -> encodedVertexDictionarySize.map(b -> a + b));\n    final var mortonDictionaryEncodedSize =\n        encodedMortonEncodedDictionaryOffsetsSize.flatMap(\n            a -> encodedMortonVertexDictionarySize.map(b -> a + b));\n    // TODO: end\n\n    final var result =\n        IntegerEncoder.encodeIntStream(\n            geometryTypes,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.LENGTH,\n            null,\n            encodingOption);\n    var numStreams = 1;\n\n    /* Currently use pre-tessellation only if all geometries in a FeatureTable are Polygons or MultiPolygons */\n    if (enableTessellation && containsOnlyPolygons(geometryTypes)) {\n      // TODO: also support Vertex Dictionary and Morton Encoded Vertex Dictionary encoding?\n      numStreams +=\n          encodePolygonPretessellationStreams(\n              result,\n              physicalLevelTechnique,\n              encodingOption,\n              numGeometries,\n              numParts,\n              numRings,\n              numTriangles,\n              indexBuffer,\n              encodePolygonOutlines);\n      result.addAll(encodedVertexBufferStream);\n      return new EncodedGeometryColumn(\n          numStreams + 1, result, vertexLimits.max, geometryColumnSorted);\n    }\n\n    if (appendLengthStream(\n        result, numGeometries, physicalLevelTechnique, LengthType.GEOMETRIES, encodingOption)) {\n      numStreams++;\n    }\n    if (appendLengthStream(\n        result, numParts, physicalLevelTechnique, LengthType.PARTS, encodingOption)) {\n      numStreams++;\n    }\n    if (appendLengthStream(\n        result, numRings, physicalLevelTechnique, LengthType.RINGS, encodingOption)) {\n      numStreams++;\n    }\n\n    @NotNull final ArrayList<byte[]> selectedVertexStream;\n    @Nullable final int[] selectedVertexOffsets;\n\n    final var dictBeatsPlain =\n        dictionaryEncodedSize.map(s -> s < plainVertexBufferSize).orElse(false);\n    final var dictBeatsMorton =\n        OptionalUtil.isLessThan(dictionaryEncodedSize, mortonDictionaryEncodedSize);\n    final var mortonBeatsPlain =\n        mortonDictionaryEncodedSize.map(s -> s < plainVertexBufferSize).orElse(false);\n    final var mortonBeatsDict =\n        OptionalUtil.isLessThan(mortonDictionaryEncodedSize, dictionaryEncodedSize);\n\n    if (dictBeatsPlain && dictBeatsMorton) {\n      selectedVertexOffsets = vertexDictionaryOffsets.get();\n      selectedVertexStream =\n          encodeVertexBuffer(zigZagDeltaVertexDictionary.get(), physicalLevelTechnique);\n      geometryColumnSorted = false;\n    } else if (mortonBeatsPlain && mortonBeatsDict) {\n      selectedVertexOffsets = mortonEncodedDictionaryOffsets.get();\n      selectedVertexStream =\n          IntegerEncoder.encodeMortonStream(\n              mortonEncodedDictionary.get(),\n              zOrderCurve.get().numBits(),\n              zOrderCurve.get().coordinateShift(),\n              physicalLevelTechnique);\n    } else {\n      selectedVertexStream = encodedVertexBufferStream;\n      selectedVertexOffsets = null;\n    }\n\n    if (selectedVertexOffsets != null && selectedVertexOffsets.length > 0) {\n      result.addAll(\n          IntegerEncoder.encodeIntStream(\n              selectedVertexOffsets,\n              physicalLevelTechnique,\n              false,\n              PhysicalStreamType.OFFSET,\n              new LogicalStreamType(OffsetType.VERTEX),\n              encodingOption));\n      numStreams++;\n    }\n    result.addAll(selectedVertexStream);\n    return new EncodedGeometryColumn(\n        numStreams + 1, result, vertexLimits.max, geometryColumnSorted);\n  }\n\n  private static record MinMax<T>(T min, T max) {}\n\n  private static MinMax<Integer> getVertexLimits(List<Vertex> vertexBuffer) {\n    var minVertexValue = Integer.MAX_VALUE;\n    var maxVertexValue = Integer.MIN_VALUE;\n    for (final var vertex : vertexBuffer) {\n      final var x = vertex.x();\n      final var y = vertex.y();\n      if (x < minVertexValue) minVertexValue = x;\n      if (y < minVertexValue) minVertexValue = y;\n      if (x > maxVertexValue) maxVertexValue = x;\n      if (y > maxVertexValue) maxVertexValue = y;\n    }\n    return new MinMax<Integer>(minVertexValue, maxVertexValue);\n  }\n\n  /// Break geometry down into encodable components\n  /// Optional tessellation overload\n  private static void prepareGeometry(\n      List<Geometry> geometries,\n      ArrayList<Integer> numGeometries,\n      ArrayList<Integer> geometryTypes,\n      ArrayList<Vertex> vertexBuffer,\n      ArrayList<Integer> numParts,\n      ArrayList<Integer> numRings,\n      ArrayList<Integer> numTriangles,\n      ArrayList<Integer> indexBuffer,\n      @Nullable URI tessellateSource) {\n    final var containsPolygon = containsPolygon(geometries);\n    final var tessellate = (numTriangles != null && indexBuffer != null);\n    for (var geometry : geometries) {\n      switch (geometry) {\n        case Point point -> {\n          geometryTypes.add(GeometryType.POINT.ordinal());\n          vertexBuffer.add(new Vertex((int) point.getX(), (int) point.getY()));\n        }\n        case LineString lineString -> {\n          // TODO: verify if part of a MultiPolygon or Polygon geometry add then to numRings?\n          geometryTypes.add(GeometryType.LINESTRING.ordinal());\n          final var numVertices = lineString.getCoordinates().length;\n          addLineString(containsPolygon, numVertices, numParts, numRings);\n          vertexBuffer.addAll(flatLineString(lineString));\n        }\n        case Polygon polygon -> {\n          geometryTypes.add(GeometryType.POLYGON.ordinal());\n          flatPolygon(polygon, vertexBuffer, numParts, numRings);\n\n          if (tessellate) {\n            final var tessellatedPolygon =\n                TessellationUtils.tessellatePolygon(polygon, 0, tessellateSource);\n            numTriangles.add(tessellatedPolygon.numTriangles());\n            indexBuffer.addAll(tessellatedPolygon.indexBuffer());\n          }\n        }\n        case MultiLineString multiLineString -> {\n          // TODO: verify if part of a MultiPolygon or Polygon geometry add then to numRings?\n          geometryTypes.add(GeometryType.MULTILINESTRING.ordinal());\n          final var numLineStrings = multiLineString.getNumGeometries();\n          numGeometries.add(numLineStrings);\n          for (var i = 0; i < numLineStrings; i++) {\n            final var lineString = (LineString) multiLineString.getGeometryN(i);\n            final var numVertices = lineString.getCoordinates().length;\n            addLineString(containsPolygon, numVertices, numParts, numRings);\n            vertexBuffer.addAll(flatLineString(lineString));\n          }\n        }\n        case MultiPolygon multiPolygon -> {\n          geometryTypes.add(GeometryType.MULTIPOLYGON.ordinal());\n          final var numPolygons = multiPolygon.getNumGeometries();\n          numGeometries.add(numPolygons);\n          for (var i = 0; i < numPolygons; i++) {\n            final var polygon = (Polygon) multiPolygon.getGeometryN(i);\n            flatPolygon(polygon, vertexBuffer, numParts, numRings);\n          }\n\n          // TODO: use also a vertex dictionary encoding for MultiPolygon geometries\n          if (tessellate) {\n            final var tessellatedPolygon =\n                TessellationUtils.tessellateMultiPolygon(multiPolygon, tessellateSource);\n            numTriangles.add(tessellatedPolygon.numTriangles());\n            indexBuffer.addAll(tessellatedPolygon.indexBuffer());\n          }\n        }\n        case MultiPoint multiPoint -> {\n          geometryTypes.add(GeometryType.MULTIPOINT.ordinal());\n          final var numPoints = multiPoint.getNumGeometries();\n          numGeometries.add(numPoints);\n          for (var i = 0; i < numPoints; i++) {\n            final var point = (Point) multiPoint.getGeometryN(i);\n            vertexBuffer.add(new Vertex((int) point.getX(), (int) point.getY()));\n          }\n        }\n        default ->\n            throw new IllegalArgumentException(\n                \"Specified geometry type is not (yet) supported: \" + geometry.getGeometryType());\n      }\n    }\n  }\n\n  private static int encodePolygonPretessellationStreams(\n      final ArrayList<byte[]> result,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      @NotNull IntegerEncodingOption encodingOption,\n      ArrayList<Integer> numGeometries,\n      ArrayList<Integer> numParts,\n      ArrayList<Integer> numRings,\n      ArrayList<Integer> numTriangles,\n      ArrayList<Integer> indexBuffer,\n      boolean withOutlines)\n      throws IOException {\n    if (withOutlines) {\n      Objects.requireNonNull(numGeometries);\n      Objects.requireNonNull(numParts);\n      Objects.requireNonNull(numRings);\n    }\n    int numStreams = 1;\n\n    // TODO: Don't write empty streams\n    final boolean forceEmpty = true;\n\n    if (withOutlines) {\n      if (appendLengthStream(\n          result,\n          numGeometries,\n          physicalLevelTechnique,\n          LengthType.GEOMETRIES,\n          encodingOption,\n          forceEmpty)) {\n        numStreams++;\n      }\n      if (appendLengthStream(\n          result, numParts, physicalLevelTechnique, LengthType.PARTS, encodingOption, forceEmpty)) {\n        numStreams++;\n      }\n      if (appendLengthStream(\n          result, numRings, physicalLevelTechnique, LengthType.RINGS, encodingOption, forceEmpty)) {\n        numStreams++;\n      }\n    }\n    if (appendLengthStream(\n        result, numTriangles, physicalLevelTechnique, LengthType.TRIANGLES, encodingOption)) {\n      numStreams++;\n    }\n    result.addAll(\n        IntegerEncoder.encodeIntStream(\n            indexBuffer,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.OFFSET,\n            new LogicalStreamType(OffsetType.INDEX),\n            encodingOption));\n    return numStreams;\n  }\n\n  private static boolean appendLengthStream(\n      @NotNull ArrayList<byte[]> result,\n      @Nullable List<Integer> values,\n      @NotNull PhysicalLevelTechnique physicalLevelTechnique,\n      @NotNull LengthType lengthType,\n      @NotNull IntegerEncodingOption encodingOption)\n      throws IOException {\n    return appendLengthStream(\n        result, values, physicalLevelTechnique, lengthType, encodingOption, false);\n  }\n\n  private static boolean appendLengthStream(\n      @NotNull ArrayList<byte[]> result,\n      @Nullable List<Integer> values,\n      @NotNull PhysicalLevelTechnique physicalLevelTechnique,\n      @NotNull LengthType lengthType,\n      @NotNull IntegerEncodingOption encodingOption,\n      boolean forceWriteEmptyStream)\n      throws IOException {\n    if (values != null && !values.isEmpty() || forceWriteEmptyStream) {\n      result.addAll(\n          IntegerEncoder.encodeIntStream(\n              (values != null) ? values : List.of(),\n              physicalLevelTechnique,\n              false,\n              PhysicalStreamType.LENGTH,\n              new LogicalStreamType(lengthType),\n              encodingOption));\n      return true;\n    }\n    return false;\n  }\n\n  private static boolean containsPolygon(List<Geometry> geometries) {\n    return geometries.stream()\n        .map(Geometry::getGeometryType)\n        .anyMatch(\n            t -> t.equals(Geometry.TYPENAME_MULTIPOLYGON) || t.equals(Geometry.TYPENAME_POLYGON));\n  }\n\n  public static boolean containsOnlyPolygons(List<Integer> geometryTypes) {\n    return geometryTypes.stream()\n        .allMatch(\n            geometryType ->\n                geometryType == GeometryType.POLYGON.ordinal()\n                    || geometryType == GeometryType.MULTIPOLYGON.ordinal());\n  }\n\n  private static void addLineString(\n      boolean containsPolygon, int numVertices, List<Integer> numParts, List<Integer> numRings) {\n    /* Depending on the max geometry type in the column add to the numRings or numParts stream */\n    if (containsPolygon) {\n      numRings.add(numVertices);\n    } else {\n      numParts.add(numVertices);\n    }\n  }\n\n  public static int[] zigZagDeltaEncodeVertices(@NotNull final Collection<Vertex> vertices) {\n    return zigZagDeltaEncodeVertices(vertices.stream(), vertices.size());\n  }\n\n  public static int[] zigZagDeltaEncodeVertices(@NotNull final Vertex[] vertices) {\n    return zigZagDeltaEncodeVertices(\n        StreamSupport.stream(Arrays.spliterator(vertices), false), vertices.length);\n  }\n\n  private static int[] zigZagDeltaEncodeVertices(\n      @NotNull final Stream<Vertex> vertices, final int size) {\n    int prevX = 0;\n    int prevY = 0;\n    int j = 0;\n    final var deltaValues = new int[size * 2];\n    for (var iter = vertices.iterator(); iter.hasNext(); ) {\n      final var vertex = iter.next();\n      final var x = vertex.x();\n      final var y = vertex.y();\n      deltaValues[j++] = EncodingUtils.encodeZigZag(x - prevX);\n      deltaValues[j++] = EncodingUtils.encodeZigZag(y - prevY);\n      prevX = x;\n      prevY = y;\n    }\n    return deltaValues;\n  }\n\n  private static int[] getVertexOffsets(\n      List<Vertex> vertexBuffer,\n      Function<Integer, Integer> vertexOffsetSupplier,\n      SpaceFillingCurve curve) {\n    int[] result = new int[vertexBuffer.size()];\n    int i = 0;\n    for (var vertex : vertexBuffer) {\n      result[i++] = vertexOffsetSupplier.apply(curve.encode(vertex));\n    }\n    return result;\n  }\n\n  private static Map<Integer, Integer> reverseMap(IntStream mortonEncodedDictionary, int size) {\n    Map<Integer, Integer> morton = HashMap.newHashMap(size);\n    int i = 0;\n    for (var iter = mortonEncodedDictionary.iterator(); iter.hasNext(); ) {\n      morton.put(iter.nextInt(), i++);\n    }\n    return morton;\n  }\n\n  private static Map<Integer, Integer> reverseMap(Collection<Integer> mortonEncodedDictionary) {\n    return reverseMap(\n        mortonEncodedDictionary.stream().mapToInt(Integer::intValue),\n        mortonEncodedDictionary.size());\n  }\n\n  private static Map<Integer, Integer> reverseMap(int[] mortonEncodedDictionary) {\n    return reverseMap(IntStream.of(mortonEncodedDictionary), mortonEncodedDictionary.length);\n  }\n\n  /// An entry in the vertex dictionary, used for sorting and filtering duplicates\n  record Indexed(int hilbert, int index) implements Comparable<Indexed> {\n    @Override\n    public int compareTo(@NotNull GeometryEncoder.Indexed o) {\n      return Integer.compare(hilbert, o.hilbert);\n    }\n  }\n\n  /// A predicate for filtering consecutive duplicates from streams of `Indexed`\n  private static Predicate<Indexed> distinctByHilbertId() {\n    return new Predicate<Indexed>() {\n      private boolean first = true;\n      private int lastSeen;\n\n      @Override\n      public boolean test(Indexed indexed) {\n        if (first || indexed.hilbert != lastSeen) {\n          lastSeen = indexed.hilbert;\n          first = false;\n          return true;\n        }\n        return false;\n      }\n    };\n  }\n\n  private static Pair<int[], Vertex[]> addVerticesToDictionary(\n      @NotNull final ArrayList<Vertex> vertices, @NotNull final HilbertCurve hilbertCurve) {\n    // 1. Convert to (hilbertId, vertex) pairs\n    // 2. Sort by hilbertId\n    // 3. Filter consecutive duplicates\n    // 4. Convert back to separate arrays for hilbertIds and vertices\n    // Can we do this without materializing the intermediate list?\n    final var vertexDictionary =\n        IntStream.range(0, vertices.size())\n            .mapToObj(i -> new Indexed(hilbertCurve.encode(vertices.get(i)), i))\n            .sorted(Comparator.naturalOrder())\n            // TODO: we currently don't filter duplicates in the vertex dictionary!\n            // .filter(distinctByHilbertId())\n            .toList();\n    return Pair.of(\n        vertexDictionary.stream().mapToInt(Indexed::hilbert).toArray(),\n        vertexDictionary.stream().map(i -> vertices.get(i.index)).toArray(Vertex[]::new));\n  }\n\n  private static int[] addVerticesToMortonDictionary(\n      @NotNull final Collection<Vertex> vertices, @NotNull final ZOrderCurve zOrderCurve) {\n    return vertices.stream().mapToInt(zOrderCurve::encode).sorted().toArray();\n  }\n\n  private static List<Vertex> flatLineString(LineString lineString) {\n    return Arrays.stream(lineString.getCoordinates())\n        .map(v -> new Vertex((int) v.x, (int) v.y))\n        .toList();\n  }\n\n  private static LineString ringToLineString(LinearRing ring, GeometryFactory factory) {\n    return factory.createLineString(\n        Arrays.copyOf(ring.getCoordinates(), ring.getCoordinates().length - 1));\n  }\n\n  private static void flatPolygon(\n      Polygon polygon, ArrayList<Vertex> vertices, List<Integer> partSize, List<Integer> ringSize) {\n    final var factory = new GeometryFactory();\n\n    // 1 for the outline, 1 for each interior ring\n    partSize.add(1 + polygon.getNumInteriorRing());\n\n    final var exteriorRing = polygon.getExteriorRing();\n    // If the ring isn't closed, our assumptions about the number of vertices will be incorrect.\n    assert (exteriorRing.isClosed());\n\n    final var shell = ringToLineString(exteriorRing, factory);\n    vertices.addAll(flatLineString(shell));\n    ringSize.add(shell.getNumPoints());\n\n    for (var i = 0; i < polygon.getNumInteriorRing(); i++) {\n      final var interiorRing = polygon.getInteriorRingN(i);\n      assert (interiorRing.isClosed());\n\n      final var ring = ringToLineString(interiorRing, factory);\n      vertices.addAll(flatLineString(ring));\n      ringSize.add(ring.getNumPoints());\n    }\n  }\n\n  /**\n   * Encodes the StreamMetadata and applies the specified physical level technique to the values.\n   */\n  private static ArrayList<byte[]> encodeVertexBuffer(\n      int[] values, PhysicalLevelTechnique physicalLevelTechnique) throws IOException {\n    final var encodedValues =\n        physicalLevelTechnique == PhysicalLevelTechnique.FAST_PFOR\n            ? encodeFastPfor(values, false)\n            : encodeVarint(values, false);\n\n    final var result =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                new LogicalStreamType(DictionaryType.VERTEX),\n                LogicalLevelTechnique.COMPONENTWISE_DELTA,\n                LogicalLevelTechnique.NONE,\n                physicalLevelTechnique,\n                values.length,\n                encodedValues.length)\n            .encode();\n\n    result.add(encodedValues);\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/IntegerEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport com.google.common.collect.Lists;\nimport com.google.common.primitives.Ints;\nimport com.google.common.primitives.Longs;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.BiFunction;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.converter.CollectionUtils;\nimport org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption;\nimport org.maplibre.mlt.metadata.stream.DictionaryType;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.LogicalStreamType;\nimport org.maplibre.mlt.metadata.stream.MortonEncodedStreamMetadata;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.RleEncodedStreamMetadata;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\n/*\n * TODO: Add sampling strategy for encoding selection\n *  -> Inspired by BTRBlock sampling strategy:\n *  - take about 1% of all data as samples\n *  - divide into ? blocks?\n * -> https://github.com/maxi-k/btrblocks/blob/c954ffd31f0873003dbc26bf1676ac460d7a3b05/btrblocks/scheme/double/RLE.cpp#L17\n * */\npublic class IntegerEncoder {\n\n  public static class IntegerEncodingResult {\n    public LogicalLevelTechnique logicalLevelTechnique1;\n    public LogicalLevelTechnique logicalLevelTechnique2;\n    public byte[] encodedValues;\n    /* If rle or delta-rle encoding is used, otherwise can be ignored */\n    public int numRuns;\n    public int physicalLevelEncodedValuesLength;\n  }\n\n  enum LogicalLevelIntegerTechnique {\n    PLAIN,\n    DELTA,\n    RLE,\n    DELTA_RLE\n  }\n\n  private IntegerEncoder() {}\n\n  public static ArrayList<byte[]> encodeMortonStream(\n      int[] values, int numBits, int coordinateShift, PhysicalLevelTechnique physicalLevelTechnique)\n      throws IOException {\n    final var encodedValueStream = encodeMortonCodes(values, physicalLevelTechnique);\n    final var valuesMetadata =\n        new MortonEncodedStreamMetadata(\n            PhysicalStreamType.DATA,\n            new LogicalStreamType(DictionaryType.MORTON),\n            encodedValueStream.logicalLevelTechnique1,\n            encodedValueStream.logicalLevelTechnique2,\n            physicalLevelTechnique,\n            encodedValueStream.physicalLevelEncodedValuesLength,\n            encodedValueStream.encodedValues.length,\n            numBits,\n            coordinateShift);\n\n    final var result = valuesMetadata.encode();\n    result.add(encodedValueStream.encodedValues);\n    return result;\n  }\n\n  // Encodes integer stream with AUTO encoding option (backward compatibility).\n  public static ArrayList<byte[]> encodeIntStream(\n      List<Integer> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType)\n      throws IOException {\n    return encodeIntStream(\n        CollectionUtils.unboxInts(values),\n        physicalLevelTechnique,\n        isSigned,\n        streamType,\n        logicalStreamType);\n  }\n\n  // Encodes integer stream with AUTO encoding option (backward compatibility).\n  public static ArrayList<byte[]> encodeIntStream(\n      int[] values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType)\n      throws IOException {\n    return encodeIntStream(\n        values,\n        physicalLevelTechnique,\n        isSigned,\n        streamType,\n        logicalStreamType,\n        IntegerEncodingOption.AUTO);\n  }\n\n  public static ArrayList<byte[]> encodeIntStream(\n      List<Integer> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType,\n      @NotNull IntegerEncodingOption encodingOption)\n      throws IOException {\n    return encodeIntStream(\n        CollectionUtils.unboxInts(values),\n        physicalLevelTechnique,\n        isSigned,\n        streamType,\n        logicalStreamType,\n        encodingOption);\n  }\n\n  public static ArrayList<byte[]> encodeIntStream(\n      int[] values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType,\n      @NotNull IntegerEncodingOption encodingOption)\n      throws IOException {\n    final var encodedValueStream =\n        IntegerEncoder.encodeInt(values, physicalLevelTechnique, isSigned, encodingOption);\n\n    // TODO: refactor -> also allow the use of none null suppression techniques\n    final var streamMetadata =\n        (encodedValueStream.logicalLevelTechnique1 == LogicalLevelTechnique.RLE\n                || encodedValueStream.logicalLevelTechnique2 == LogicalLevelTechnique.RLE)\n            ? new RleEncodedStreamMetadata(\n                streamType,\n                logicalStreamType,\n                encodedValueStream.logicalLevelTechnique1,\n                encodedValueStream.logicalLevelTechnique2,\n                physicalLevelTechnique,\n                encodedValueStream.physicalLevelEncodedValuesLength,\n                encodedValueStream.encodedValues.length,\n                encodedValueStream.numRuns,\n                values.length)\n            : new StreamMetadata(\n                streamType,\n                logicalStreamType,\n                encodedValueStream.logicalLevelTechnique1,\n                encodedValueStream.logicalLevelTechnique2,\n                physicalLevelTechnique,\n                encodedValueStream.physicalLevelEncodedValuesLength,\n                encodedValueStream.encodedValues.length);\n    final var result = streamMetadata.encode();\n    result.add(encodedValueStream.encodedValues);\n    return result;\n  }\n\n  // Encodes long stream with AUTO encoding option (backward compatibility).\n  public static ArrayList<byte[]> encodeLongStream(\n      long[] values,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType)\n      throws IOException {\n    return encodeLongStream(\n        values, isSigned, streamType, logicalStreamType, IntegerEncodingOption.AUTO);\n  }\n\n  public static ArrayList<byte[]> encodeLongStream(\n      long[] values,\n      boolean isSigned,\n      PhysicalStreamType streamType,\n      LogicalStreamType logicalStreamType,\n      @NotNull IntegerEncodingOption encodingOption)\n      throws IOException {\n    final var encodedValueStream = IntegerEncoder.encodeLong(values, isSigned, encodingOption);\n\n    /* Currently FastPfor is only supported with 32 bit so for long we always have to fallback to Varint encoding */\n    final var streamMetadata =\n        (encodedValueStream.logicalLevelTechnique1 == LogicalLevelTechnique.RLE\n                || encodedValueStream.logicalLevelTechnique2 == LogicalLevelTechnique.RLE)\n            ? new RleEncodedStreamMetadata(\n                streamType,\n                logicalStreamType,\n                encodedValueStream.logicalLevelTechnique1,\n                encodedValueStream.logicalLevelTechnique2,\n                PhysicalLevelTechnique.VARINT,\n                encodedValueStream.physicalLevelEncodedValuesLength,\n                encodedValueStream.encodedValues.length,\n                encodedValueStream.numRuns,\n                values.length)\n            : new StreamMetadata(\n                streamType,\n                logicalStreamType,\n                encodedValueStream.logicalLevelTechnique1,\n                encodedValueStream.logicalLevelTechnique2,\n                PhysicalLevelTechnique.VARINT,\n                encodedValueStream.physicalLevelEncodedValuesLength,\n                encodedValueStream.encodedValues.length);\n    final var result = streamMetadata.encode();\n    result.add(encodedValueStream.encodedValues);\n    return result;\n  }\n\n  // TODO: make dependent on specified LogicalLevelTechnique\n  public static IntegerEncodingResult encodeMortonCodes(\n      int[] values, PhysicalLevelTechnique physicalLevelTechnique) throws IOException {\n    var previousValue = 0;\n    int[] deltaValues = new int[values.length];\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      var delta = value - previousValue;\n      deltaValues[i] = delta;\n      previousValue = value;\n    }\n\n    var encodedValues =\n        physicalLevelTechnique == PhysicalLevelTechnique.FAST_PFOR\n            ? encodeFastPfor(deltaValues, false)\n            : EncodingUtils.encodeVarints(deltaValues, false, false);\n\n    var result = new IntegerEncodingResult();\n    result.logicalLevelTechnique1 = LogicalLevelTechnique.MORTON;\n    result.logicalLevelTechnique2 = LogicalLevelTechnique.DELTA;\n    result.physicalLevelEncodedValuesLength = values.length;\n    result.numRuns = 0;\n    result.encodedValues = encodedValues;\n    return result;\n  }\n\n  // Encodes integers with AUTO encoding option (backward compatibility).\n  public static IntegerEncodingResult encodeInt(\n      int[] values, PhysicalLevelTechnique physicalLevelTechnique, boolean isSigned) {\n    return encodeInt(values, physicalLevelTechnique, isSigned, IntegerEncodingOption.AUTO);\n  }\n\n  /*\n   * Integers are encoded based on the two lightweight compression techniques delta and rle as well\n   * as a combination of both schemes called delta-rle.\n   * */\n  public static IntegerEncodingResult encodeInt(\n      int[] values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      @NotNull IntegerEncodingOption encodingOption) {\n    var previousValue = 0;\n    var previousDelta = 0;\n    var runs = 1;\n    var deltaRuns = 1;\n    var deltaValues = new int[values.length];\n    for (int i = 0; i < values.length; i++) {\n      int value = values[i];\n      var delta = value - previousValue;\n      deltaValues[i] = delta;\n\n      if (value != previousValue && i != 0) {\n        runs++;\n      }\n\n      if (delta != previousDelta && i != 0) {\n        deltaRuns++;\n      }\n\n      previousValue = value;\n      previousDelta = delta;\n    }\n\n    BiFunction<int[], Boolean, byte[]> encoder =\n        physicalLevelTechnique == PhysicalLevelTechnique.FAST_PFOR\n            ? IntegerEncoder::encodeFastPfor\n            : (v, s) -> {\n              try {\n                return EncodingUtils.encodeVarints(v, s, false);\n              } catch (IOException e) {\n                throw new RuntimeException(e);\n              }\n            };\n\n    // Early return for forced PLAIN encoding\n    if (encodingOption == IntegerEncodingOption.PLAIN) {\n      var result = new IntegerEncodingResult();\n      result.encodedValues = encoder.apply(values, isSigned);\n      result.physicalLevelEncodedValuesLength = values.length;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.NONE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n      return result;\n    }\n\n    // Early return for forced DELTA encoding\n    if (encodingOption == IntegerEncodingOption.DELTA) {\n      var result = new IntegerEncodingResult();\n      result.encodedValues = encoder.apply(deltaValues, true);\n      result.physicalLevelEncodedValuesLength = values.length;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n      return result;\n    }\n\n    var plainEncodedValues = encoder.apply(values, isSigned);\n    var deltaEncodedValues = encoder.apply(deltaValues, true);\n    var encodedValues = Lists.newArrayList(plainEncodedValues, deltaEncodedValues);\n    byte[] rleEncodedValues = null;\n    byte[] deltaRleEncodedValues = null;\n    var rlePhysicalLevelEncodedValuesLength = 0;\n    var deltaRlePhysicalLevelEncodedValuesLength = 0;\n\n    /* Use selection logic from BTR Blocks -> https://github.com/maxi-k/btrblocks/blob/c954ffd31f0873003dbc26bf1676ac460d7a3b05/btrblocks/scheme/double/RLE.cpp#L17 */\n    /*\n     * if there are ony a view values (e.g. 4 times 1) rle only produces the same size then other\n     * encodings. Since we want to force that all const streams use RLE encoding we use this current\n     * workaround\n     */\n    var isConstStream = false;\n    if (values.length / runs >= 2\n        && (encodingOption == IntegerEncodingOption.AUTO\n            || encodingOption == IntegerEncodingOption.RLE)) {\n      var rleValues = EncodingUtils.encodeRle(values);\n      rlePhysicalLevelEncodedValuesLength =\n          rleValues.getLeft().length + rleValues.getRight().length;\n      rleEncodedValues =\n          encoder.apply(\n              Ints.concat(\n                  rleValues.getLeft(),\n                  isSigned\n                      ? EncodingUtils.encodeZigZag(rleValues.getRight())\n                      : rleValues.getRight()),\n              false);\n      isConstStream = rleValues.getLeft().length == 1;\n\n      // Early return for forced RLE encoding\n      if (encodingOption == IntegerEncodingOption.RLE) {\n        var result = new IntegerEncodingResult();\n        result.encodedValues = rleEncodedValues;\n        result.physicalLevelEncodedValuesLength = rlePhysicalLevelEncodedValuesLength;\n        result.numRuns = runs;\n        result.logicalLevelTechnique1 = LogicalLevelTechnique.RLE;\n        result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n        return result;\n      }\n    }\n\n    if (deltaValues.length / deltaRuns >= 2) {\n      // TODO: get rid of conversion\n      var deltaRleValues = EncodingUtils.encodeRle(deltaValues);\n      deltaRlePhysicalLevelEncodedValuesLength =\n          deltaRleValues.getLeft().length + deltaRleValues.getRight().length;\n      var zigZagDelta = EncodingUtils.encodeZigZag(deltaRleValues.getRight());\n      // TODO: encode runs and length separate?\n      deltaRleEncodedValues =\n          encoder.apply(Ints.concat(deltaRleValues.getLeft(), zigZagDelta), false);\n\n      // Early return for forced DELTA_RLE encoding\n      if (encodingOption == IntegerEncodingOption.DELTA_RLE) {\n        var result = new IntegerEncodingResult();\n        result.encodedValues = deltaRleEncodedValues;\n        result.physicalLevelEncodedValuesLength = deltaRlePhysicalLevelEncodedValuesLength;\n        result.numRuns = deltaRuns;\n        result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n        result.logicalLevelTechnique2 = LogicalLevelTechnique.RLE;\n        return result;\n      }\n    }\n\n    encodedValues.add(rleEncodedValues);\n    encodedValues.add(deltaRleEncodedValues);\n\n    // TODO: refactor -> find proper solution\n    final var encodedValuesSizes =\n        encodedValues.stream().map(v -> v == null ? Integer.MAX_VALUE : v.length).toList();\n    final var index =\n        isConstStream\n            ? LogicalLevelIntegerTechnique.RLE.ordinal()\n            : encodedValuesSizes.indexOf(Collections.min(encodedValuesSizes));\n    final var encoding = LogicalLevelIntegerTechnique.values()[index];\n\n    final var result = new IntegerEncodingResult();\n    result.encodedValues = encodedValues.get(index);\n    result.physicalLevelEncodedValuesLength = values.length;\n    if (encoding == LogicalLevelIntegerTechnique.RLE || isConstStream) {\n      result.numRuns = runs;\n      result.physicalLevelEncodedValuesLength = rlePhysicalLevelEncodedValuesLength;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.RLE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    } else if (encoding == LogicalLevelIntegerTechnique.DELTA_RLE) {\n      result.numRuns = deltaRuns;\n      result.physicalLevelEncodedValuesLength = deltaRlePhysicalLevelEncodedValuesLength;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.RLE;\n    } else if (encoding == LogicalLevelIntegerTechnique.DELTA) {\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    } else {\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.NONE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    }\n\n    return result;\n  }\n\n  // Encodes long values with AUTO encoding option (backward compatibility).\n  public static IntegerEncodingResult encodeLong(long[] values, boolean isSigned) {\n    return encodeLong(values, isSigned, IntegerEncodingOption.AUTO);\n  }\n\n  public static IntegerEncodingResult encodeLong(\n      long[] values, boolean isSigned, @NotNull IntegerEncodingOption encodingOption) {\n    var previousValue = 0L;\n    var previousDelta = 0L;\n    var runs = 1;\n    var deltaRuns = 1;\n    var deltaValues = new long[values.length];\n    for (var i = 0; i < values.length; i++) {\n      var value = values[i];\n      var delta = value - previousValue;\n      deltaValues[i] = delta;\n\n      if (value != previousValue && i != 0) {\n        runs++;\n      }\n\n      if (delta != previousDelta && i != 0) {\n        deltaRuns++;\n      }\n\n      previousValue = value;\n      previousDelta = delta;\n    }\n\n    BiFunction<long[], Boolean, byte[]> encoder =\n        (v, s) -> {\n          try {\n            return EncodingUtils.encodeLongVarints(v, s, false);\n          } catch (IOException e) {\n            throw new RuntimeException(e);\n          }\n        };\n\n    if (encodingOption == IntegerEncodingOption.PLAIN) {\n      final var result = new IntegerEncodingResult();\n      result.encodedValues = encoder.apply(values, isSigned);\n      result.physicalLevelEncodedValuesLength = values.length;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.NONE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n      return result;\n    }\n\n    if (encodingOption == IntegerEncodingOption.DELTA) {\n      final var result = new IntegerEncodingResult();\n      result.encodedValues = encoder.apply(deltaValues, true);\n      result.physicalLevelEncodedValuesLength = values.length;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n      return result;\n    }\n\n    final var plainEncodedValues = encoder.apply(values, isSigned);\n    final var deltaEncodedValues = encoder.apply(deltaValues, true);\n    final var encodedValues = Lists.newArrayList(plainEncodedValues, deltaEncodedValues);\n\n    byte[] rleEncodedValues = null;\n    byte[] deltaRleEncodedValues = null;\n    var rlePhysicalLevelEncodedValuesLength = 0;\n    var deltaRlePhysicalLevelEncodedValuesLength = 0;\n    var isConstStream = false;\n\n    /* Use selection logic from BTR Blocks -> https://github.com/maxi-k/btrblocks/blob/c954ffd31f0873003dbc26bf1676ac460d7a3b05/btrblocks/scheme/double/RLE.cpp#L17 */\n    if (values.length / runs >= 2\n        && (encodingOption == IntegerEncodingOption.AUTO\n            || encodingOption == IntegerEncodingOption.RLE)) {\n      // TODO: get rid of conversion\n      final var rleValues = EncodingUtils.encodeRle(values);\n      rlePhysicalLevelEncodedValuesLength =\n          rleValues.getLeft().length + rleValues.getRight().length;\n      rleEncodedValues =\n          encoder.apply(\n              Longs.concat(\n                  rleValues.getLeft(),\n                  isSigned\n                      ? EncodingUtils.encodeZigZag(rleValues.getRight())\n                      : rleValues.getRight()),\n              false);\n      isConstStream = rleValues.getLeft().length == 1;\n\n      if (encodingOption == IntegerEncodingOption.RLE) {\n        final var result = new IntegerEncodingResult();\n        result.encodedValues = rleEncodedValues;\n        result.physicalLevelEncodedValuesLength = rlePhysicalLevelEncodedValuesLength;\n        result.numRuns = runs;\n        result.logicalLevelTechnique1 = LogicalLevelTechnique.RLE;\n        result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n        return result;\n      }\n    }\n\n    if (deltaValues.length / deltaRuns >= 2) {\n      // TODO: get rid of conversion\n      final var deltaRleValues = EncodingUtils.encodeRle(deltaValues);\n      deltaRlePhysicalLevelEncodedValuesLength =\n          deltaRleValues.getLeft().length + deltaRleValues.getRight().length;\n      final var zigZagDelta = EncodingUtils.encodeZigZag(deltaRleValues.getRight());\n      // TODO: encode runs and length separate?\n      deltaRleEncodedValues =\n          encoder.apply(Longs.concat(deltaRleValues.getLeft(), zigZagDelta), false);\n\n      if (encodingOption == IntegerEncodingOption.DELTA_RLE) {\n        final var result = new IntegerEncodingResult();\n        result.encodedValues = deltaRleEncodedValues;\n        result.physicalLevelEncodedValuesLength = deltaRlePhysicalLevelEncodedValuesLength;\n        result.numRuns = deltaRuns;\n        result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n        result.logicalLevelTechnique2 = LogicalLevelTechnique.RLE;\n        return result;\n      }\n    }\n\n    encodedValues.add(rleEncodedValues);\n    encodedValues.add(deltaRleEncodedValues);\n\n    // TODO: refactor -> find proper solution\n    final var encodedValuesSizes =\n        encodedValues.stream().map(v -> v == null ? Integer.MAX_VALUE : v.length).toList();\n    final var index =\n        isConstStream\n            ? LogicalLevelIntegerTechnique.RLE.ordinal()\n            : encodedValuesSizes.indexOf(Collections.min(encodedValuesSizes));\n    final var encoding = LogicalLevelIntegerTechnique.values()[index];\n\n    final var result = new IntegerEncodingResult();\n    result.encodedValues = encodedValues.get(index);\n    result.physicalLevelEncodedValuesLength = values.length;\n    if (encoding == LogicalLevelIntegerTechnique.RLE || isConstStream) {\n      result.numRuns = runs;\n      result.physicalLevelEncodedValuesLength = rlePhysicalLevelEncodedValuesLength;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.RLE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    } else if (encoding == LogicalLevelIntegerTechnique.DELTA_RLE) {\n      result.numRuns = deltaRuns;\n      result.physicalLevelEncodedValuesLength = deltaRlePhysicalLevelEncodedValuesLength;\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.RLE;\n    } else if (encoding == LogicalLevelIntegerTechnique.DELTA) {\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.DELTA;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    } else {\n      result.logicalLevelTechnique1 = LogicalLevelTechnique.NONE;\n      result.logicalLevelTechnique2 = LogicalLevelTechnique.NONE;\n    }\n\n    return result;\n  }\n\n  public static byte[] encodeFastPfor(int[] values, boolean signed) {\n    return EncodingUtils.encodeFastPfor128(values, signed, false);\n  }\n\n  public static byte[] encodeVarint(int[] values, boolean signed) throws IOException {\n    return EncodingUtils.encodeVarints(values, signed, false);\n  }\n\n  public static byte[] encodeLongVarint(long[] values, boolean signed) throws IOException {\n    return EncodingUtils.encodeLongVarints(values, signed, false);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/LinearRegression.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.util.Arrays;\n\n// Source: https://github.com/yhliu918/Learn-to-Compress\npublic class LinearRegression {\n\n  public static double[] calculateDeltas(double[] indices, double[] actualValues, double[] theta) {\n    var predictions = calculatePredictions(indices, theta);\n\n    var deltas = new double[actualValues.length];\n    for (var i = 0; i < actualValues.length; i++) {\n      deltas[i] = Math.abs(predictions[i] - actualValues[i]);\n    }\n\n    return deltas;\n  }\n\n  public static double[] calculatePredictions(double[] x, double[] theta) {\n    int m = x.length;\n    double[] predictions = new double[m];\n    for (int i = 0; i < m; ++i) {\n      predictions[i] = h(x[i], theta);\n    }\n    return predictions;\n  }\n\n  private static double h(double x, double[] theta) {\n    return theta[0] + theta[1] * x;\n  }\n\n  public static double computeCost(double[] x, double[] y, double[] theta) {\n    int m = x.length;\n    double[] predictions = calculatePredictions(x, theta);\n    double[] diff = arrayDiff(predictions, y);\n    double[] sqErrors = arrayPow(diff, 2);\n    return (1.0 / (2 * m)) * arraySum(sqErrors);\n  }\n\n  public static double[] gradientDescent(\n      double[] x, double[] y, double alpha, int iters, double[] J) {\n    int m = x.length;\n    // int m = 1;\n    double[] theta = new double[2];\n    theta[1] = (y[m - 1] - y[0]) / (x[m - 1] - x[0]);\n    theta[0] = y[0] - theta[1] * x[0];\n    for (int i = 0; i < iters; ++i) {\n      double[] predictions = calculatePredictions(x, theta);\n      double[] diff = arrayDiff(predictions, y);\n      double[] errorsX1 = diff;\n      double[] errorsX2 = arrayMultiplication(diff, x);\n      theta[0] -= alpha * (1.0 / m) * arraySum(errorsX1);\n      theta[1] -= alpha * (1.0 / m) * arraySum(errorsX2);\n      J[i] = computeCost(x, y, theta);\n    }\n    return theta;\n  }\n\n  public static double[] arrayDiff(double[] arr1, double[] arr2) {\n    int len = arr1.length;\n    double[] arr = new double[len];\n    for (int i = 0; i < len; ++i) {\n      arr[i] = arr1[i] - arr2[i];\n    }\n    return arr;\n  }\n\n  public static double[] arrayPow(double[] arr, int power) {\n    int len = arr.length;\n    double[] arr2 = new double[len];\n    for (int i = 0; i < len; ++i) {\n      arr2[i] = Math.pow(arr[i], power);\n    }\n    return arr2;\n  }\n\n  public static double[] arrayMultiplication(double[] arr1, double[] arr2) {\n    int len = arr1.length;\n    double[] arr = new double[len];\n    for (int i = 0; i < len; ++i) {\n      arr[i] = arr1[i] * arr2[i];\n    }\n    return arr;\n  }\n\n  public static double arraySum(double[] arr) {\n    return Arrays.stream(arr).sum();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/MltTypeMap.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport jakarta.annotation.Nullable;\nimport java.util.List;\nimport java.util.Optional;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic class MltTypeMap {\n  public static class Tag0x01 {\n    /// Produces the unique type encoding for a `Column` or `Field`\n    /// @param physicalScalarType The physical scalar type, if applicable\n    /// @param logicalScalarType The logical scalar type, if applicable\n    /// @param physicalComplexType The physical complex type, if applicable\n    /// @param ignoredLogicalComplexType not currently used\n    /// @param isNullable Whether the column is nullable\n    /// @param hasChildren Whether the column has children (i.e. is a struct)\n    /// @param hasLongIDs Whether the column has long IDs (i.e. 64-bit vs 32-bit)\n    /// @return A unique integer encoding of the column type, if supported by the encoding scheme\n    public static Optional<Integer> encodeColumnType(\n        @Nullable MltMetadata.ScalarType physicalScalarType,\n        @Nullable MltMetadata.LogicalScalarType logicalScalarType,\n        @Nullable MltMetadata.ComplexType physicalComplexType,\n        @Nullable MltMetadata.LogicalComplexType ignoredLogicalComplexType,\n        boolean isNullable,\n        boolean hasChildren,\n        boolean hasLongIDs) {\n      if (physicalScalarType != null) {\n        if (!hasChildren) {\n          return switch (physicalScalarType) {\n            case BOOLEAN -> Optional.of(isNullable ? 11 : 10);\n            case INT_8 -> Optional.of(isNullable ? 13 : 12);\n            case UINT_8 -> Optional.of(isNullable ? 15 : 14);\n            case INT_32 -> Optional.of(isNullable ? 17 : 16);\n            case UINT_32 -> Optional.of(isNullable ? 19 : 18);\n            case INT_64 -> Optional.of(isNullable ? 21 : 20);\n            case UINT_64 -> Optional.of(isNullable ? 23 : 22);\n            case FLOAT -> Optional.of(isNullable ? 25 : 24);\n            case DOUBLE -> Optional.of(isNullable ? 27 : 26);\n            case STRING -> Optional.of(isNullable ? 29 : 28);\n            default -> Optional.empty();\n          };\n        }\n      } else if (logicalScalarType != null) {\n        if (logicalScalarType == MltMetadata.LogicalScalarType.ID) {\n          return Optional.of(isNullable ? (hasLongIDs ? 3 : 1) : (hasLongIDs ? 2 : 0));\n        }\n      } else if (physicalComplexType != null) {\n        if (physicalComplexType == MltMetadata.ComplexType.GEOMETRY) {\n          if (!isNullable && !hasChildren) {\n            return Optional.of(4);\n          }\n        } else if (physicalComplexType == MltMetadata.ComplexType.STRUCT) {\n          if (!isNullable && hasChildren) {\n            return Optional.of(30);\n          }\n        }\n      }\n      return Optional.empty();\n    }\n\n    /// Re-create a `Column` from the unique type code.\n    /// The inverse of `getTag1TypeEncoding``\n    /// @param typeCode the unique type code encoding the column's type\n    /// @return The equivalent type descriptor\n    public static MltMetadata.FieldType decodeColumnType(int typeCode) {\n      if (10 <= typeCode && typeCode <= 29) {\n        final var isNullable = (typeCode & 1) != 0;\n        return MltMetadata.scalarFieldType(getScalarType(typeCode), isNullable);\n      } else if (0 <= typeCode && typeCode <= 3) {\n        final var isNullable = (typeCode & 1) != 0;\n        final var hasLongId = (typeCode > 1);\n        return MltMetadata.idFieldType(hasLongId, isNullable);\n      } else if (4 == typeCode) {\n        return MltMetadata.geometryFieldType();\n      } else if (30 == typeCode) {\n        return MltMetadata.structFieldType((List<MltMetadata.Field>) null);\n      } else {\n        throw new IllegalStateException(\"Unsupported Type \" + typeCode);\n      }\n    }\n\n    private static MltMetadata.ScalarType getScalarType(int typeCode) {\n      return switch (typeCode) {\n        case 10, 11 -> MltMetadata.ScalarType.BOOLEAN;\n        case 12, 13 -> MltMetadata.ScalarType.INT_8;\n        case 14, 15 -> MltMetadata.ScalarType.UINT_8;\n        case 16, 17 -> MltMetadata.ScalarType.INT_32;\n        case 18, 19 -> MltMetadata.ScalarType.UINT_32;\n        case 20, 21 -> MltMetadata.ScalarType.INT_64;\n        case 22, 23 -> MltMetadata.ScalarType.UINT_64;\n        case 24, 25 -> MltMetadata.ScalarType.FLOAT;\n        case 26, 27 -> MltMetadata.ScalarType.DOUBLE;\n        case 28, 29 -> MltMetadata.ScalarType.STRING;\n        default -> throw new IllegalStateException(\"Unsupported Type\");\n      };\n    }\n\n    public static boolean columnTypeHasName(int typeCode) {\n      return (10 <= typeCode);\n    }\n\n    public static boolean columnTypeHasChildren(int typeCode) {\n      return (typeCode == 30);\n    }\n\n    public static boolean isID(MltMetadata.Column column) {\n      return isID(column.field().type());\n    }\n\n    public static boolean isID(MltMetadata.FieldType type) {\n      return type.is(MltMetadata.LogicalScalarType.ID);\n    }\n\n    public static boolean isGeometry(MltMetadata.Column column) {\n      return isGeometry(column.field().type());\n    }\n\n    public static boolean isGeometry(MltMetadata.FieldType type) {\n      return type.is(MltMetadata.ComplexType.GEOMETRY);\n    }\n\n    public static boolean isStruct(MltMetadata.Column column) {\n      return isStruct(column.field().type());\n    }\n\n    public static boolean isStruct(MltMetadata.FieldType type) {\n      return type.is(MltMetadata.ComplexType.STRUCT);\n    }\n\n    public static boolean hasStreamCount(MltMetadata.Column column) {\n      return hasStreamCount(column.field().type());\n    }\n\n    public static boolean hasStreamCount(MltMetadata.FieldType type) {\n      if (type.scalarType() != null) {\n        final var scalar = type.scalarType();\n        if (scalar.physicalType() != null) {\n          switch (scalar.physicalType()) {\n            case BOOLEAN:\n            case INT_8:\n            case UINT_8:\n            case INT_32:\n            case UINT_32:\n            case INT_64:\n            case UINT_64:\n            case FLOAT:\n            case DOUBLE:\n              return false;\n            case STRING:\n              return true;\n            default:\n          }\n        } else if (scalar.logicalType() != null) {\n          if (scalar.logicalType() == MltMetadata.LogicalScalarType.ID) {\n            return false;\n          }\n        }\n      } else if (type.complexType() != null) {\n        final var complex = type.complexType();\n        if (complex.physicalType() != null) {\n          switch (complex.physicalType()) {\n            case GEOMETRY:\n            case STRUCT:\n              return true;\n            default:\n          }\n        }\n        // Complex/Logical not currently used\n      }\n      assert false;\n      return false;\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/PropertyEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport jakarta.annotation.Nullable;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.SequencedCollection;\nimport java.util.stream.Collectors;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.converter.CollectionUtils;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.unsigned.U32;\nimport org.maplibre.mlt.data.unsigned.U64;\nimport org.maplibre.mlt.data.unsigned.U8;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\nimport org.maplibre.mlt.util.StreamUtil;\n\npublic class PropertyEncoder {\n\n  public static ArrayList<byte[]> encodePropertyColumns(\n      SequencedCollection<MltMetadata.Column> propertyColumns,\n      SequencedCollection<Feature> features,\n      boolean useFastPFOR,\n      boolean useFSST,\n      boolean coercePropertyValues,\n      @Nullable SequencedCollection<ColumnMapping> columnMappings,\n      @NotNull ConversionConfig.IntegerEncodingOption integerEncodingOption)\n      throws IOException {\n    /*\n     * TODOs: - detect if column is nullable to get rid of the present stream - test boolean rle\n     * against roaring bitmaps and integer encoding for present stream and boolean values - Add\n     * vector type to field metadata\n     */\n    final var physicalLevelTechnique =\n        useFastPFOR ? PhysicalLevelTechnique.FAST_PFOR : PhysicalLevelTechnique.VARINT;\n    final var estimatedBuffers = propertyColumns.size() * 5;\n    final var featureScopedPropertyColumns = new ArrayList<byte[]>(estimatedBuffers);\n\n    final var columnMappingsIterator = columnMappings.iterator();\n    for (var columnMetadata : propertyColumns) {\n      final ArrayList<byte[]> encodedColumn;\n      if (columnMetadata.isScalar()) {\n        encodedColumn =\n            encodeScalarPropertyColumn(\n                features,\n                useFSST,\n                coercePropertyValues,\n                columnMetadata,\n                physicalLevelTechnique,\n                integerEncodingOption);\n      } else if (MltTypeMap.Tag0x01.isStruct(columnMetadata)) {\n        if (!columnMappingsIterator.hasNext()) {\n          throw new IllegalArgumentException(\n              \"Missing column mapping for nested property column \" + columnMetadata.getName());\n        }\n        final var columnMapping = columnMappingsIterator.next();\n        encodedColumn =\n            encodeStructPropertyColumn(\n                features, useFSST, columnMetadata, columnMapping, physicalLevelTechnique);\n      } else {\n        throw new IllegalArgumentException(\n            \"The specified data type for the field is currently not supported: \" + columnMetadata);\n      }\n\n      featureScopedPropertyColumns.addAll(encodedColumn);\n    }\n\n    return featureScopedPropertyColumns;\n  }\n\n  private static ArrayList<byte[]> encodeStructPropertyColumn(\n      SequencedCollection<Feature> features,\n      boolean useFSST,\n      MltMetadata.Column columnMetadata,\n      ColumnMapping columnMapping,\n      PhysicalLevelTechnique physicalLevelTechnique)\n      throws IOException {\n    // TODO: add present stream for struct column\n\n    /* We limit the nesting level to one in this implementation */\n    final var rootName = columnMetadata.getName();\n\n    if (!columnMapping.getUseSharedDictionaryEncoding()) {\n      throw new IllegalArgumentException(\n          \"Only shared dictionary encoding is currently supported for nested property columns\");\n    }\n\n    /* Plan -> when there is a struct field and the useSharedDictionaryFlag is enabled\n     *  share the dictionary for all string columns which are located one after\n     * the other in the sequence */\n    final var complexType = columnMetadata.field().type().complexType();\n    final var sharedDictionary =\n        new ArrayList<List<String>>(features.size() * complexType.children().size());\n    for (var nestedFieldMetadata : complexType.children()) {\n      if (nestedFieldMetadata.type().scalarType() == null) {\n        throw new IllegalArgumentException(\n            \"Nested field '\" + nestedFieldMetadata.name() + \"' has null scalarType\");\n      }\n      final var scalarType = nestedFieldMetadata.type().scalarType().physicalType();\n      if (scalarType != MltMetadata.ScalarType.STRING) {\n        throw new IllegalArgumentException(\n            \"Only fields of type String are currently supported as nested property columns\");\n      }\n\n      final var propertyName = rootName + nestedFieldMetadata.name();\n      sharedDictionary.add(\n          features.stream()\n              .map(\n                  mvtFeature ->\n                      mvtFeature\n                          .findProperty(propertyName)\n                          .filter(p -> p.getType().is(MltMetadata.ScalarType.STRING))\n                          .map(p -> p.getValue(mvtFeature.getIndex()))\n                          .flatMap(StreamUtil.optionalOfType(String.class))\n                          .orElse(null))\n              .collect(Collectors.toList()));\n    }\n\n    if (sharedDictionary.stream().allMatch(List::isEmpty)) {\n      // Set number of streams to zero if no property values are present in this tile\n      // TODO: Can we skip the column entirely in this case?\n      return new ArrayList<>(Arrays.asList(new byte[] {0}));\n    }\n    final var nestedColumns =\n        StringEncoder.encodeSharedDictionary(sharedDictionary, physicalLevelTechnique, useFSST);\n    final var numStreams = nestedColumns.getLeft();\n    final var encodedColumns = nestedColumns.getRight();\n    assert (numStreams > 0); // encodeSharedDictionary cannot return zero streams\n\n    final var result = new ArrayList<byte[]>(encodedColumns.size() + 1);\n    result.add(EncodingUtils.encodeVarint(numStreams, false));\n    result.addAll(encodedColumns);\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeScalarPropertyColumn(\n      SequencedCollection<Feature> features,\n      boolean useFSST,\n      boolean coercePropertyValues,\n      MltMetadata.Column columnMetadata,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      @NotNull ConversionConfig.IntegerEncodingOption integerEncodingOption)\n      throws IOException {\n    if (MltTypeMap.Tag0x01.hasStreamCount(columnMetadata)\n        && features.stream().noneMatch(f -> f.findProperty(columnMetadata.getName()).isPresent())) {\n      // Indicate a missing property column in the tile with a zero for the number of streams\n      // TODO: Can we skip the column entirely in this case?\n      return new ArrayList<>(Arrays.asList(new byte[] {0}));\n    }\n\n    return encodeScalarPropertyColumn(\n        columnMetadata,\n        false,\n        features,\n        physicalLevelTechnique,\n        useFSST,\n        coercePropertyValues,\n        integerEncodingOption);\n  }\n\n  private static Boolean getBooleanPropertyValue(@NotNull Feature feature, @NotNull String name) {\n    return feature\n        .findProperty(name)\n        .filter(p -> p.getType().is(MltMetadata.ScalarType.BOOLEAN))\n        .map(p -> (Boolean) p.getValue(feature.getIndex()))\n        .orElse(null);\n  }\n\n  private static Integer strictIntOrNull(@Nullable Long value) {\n    return (value != null && value.intValue() == value.longValue()) ? value.intValue() : null;\n  }\n\n  private static Integer strictIntOrNull(@Nullable U64 value) {\n    return (value != null && value.intValue().longValue() == value.longValue())\n        ? value.intValue()\n        : null;\n  }\n\n  private static Integer strictIntOrNull(@Nullable Float value) {\n    return (value != null && value.intValue() == value) ? value.intValue() : null;\n  }\n\n  private static Integer strictIntOrNull(@Nullable Double value) {\n    return (value != null && value.intValue() == value) ? value.intValue() : null;\n  }\n\n  private static Long strictLongOrNull(@Nullable Float value) {\n    return (value != null && value.longValue() == value) ? value.longValue() : null;\n  }\n\n  private static Long strictLongOrNull(@Nullable Double value) {\n    return (value != null && value.longValue() == value) ? value.longValue() : null;\n  }\n\n  private static Float strictFloatOrNull(@Nullable Double value) {\n    return (value != null && value.floatValue() == value) ? value.floatValue() : null;\n  }\n\n  private static Integer getIntPropertyValue(@NotNull Feature feature, @NotNull String name) {\n    final var index = feature.getIndex();\n    return feature\n        .findProperty(name)\n        .map(\n            p ->\n                switch (p.getType().getScalarType().orElse(null)) {\n                  case BOOLEAN -> ((Boolean) p.getValue(index)) ? 1 : 0;\n                  case UINT_8 -> ((U8) p.getValue(index)).intValue();\n                  case INT_8, INT_32 -> ((Number) p.getValue(index)).intValue();\n                  case UINT_32 -> ((U32) p.getValue(index)).intValue();\n                  case INT_64 -> strictIntOrNull((Long) p.getValue(index));\n                  case UINT_64 -> strictIntOrNull((U64) p.getValue(index));\n                  case FLOAT -> strictIntOrNull((Float) p.getValue(index));\n                  case DOUBLE -> strictIntOrNull((Double) p.getValue(index));\n                  default -> null;\n                })\n        .orElse(null);\n  }\n\n  private static Long getLongPropertyValue(@NotNull Feature feature, @NotNull String name) {\n    final var index = feature.getIndex();\n    return feature\n        .findProperty(name)\n        .map(\n            p ->\n                switch (p.getType().getScalarType().orElse(null)) {\n                  case BOOLEAN -> ((Boolean) p.getValue(index)) ? 1L : 0L;\n                  case UINT_8 -> ((U8) p.getValue(index)).longValue();\n                  case UINT_32 -> ((U32) p.getValue(index)).longValue();\n                  case INT_8, INT_32, INT_64 -> ((Number) p.getValue(index)).longValue();\n                  case UINT_64 -> ((U64) p.getValue(index)).longValue();\n                  case FLOAT -> strictLongOrNull((Float) p.getValue(index));\n                  case DOUBLE -> strictLongOrNull((Double) p.getValue(index));\n                  default -> null;\n                })\n        .orElse(null);\n  }\n\n  private static Float getFloatPropertyValue(@NotNull Feature feature, @NotNull String name) {\n    final var index = feature.getIndex();\n    return feature\n        .findProperty(name)\n        .map(\n            p ->\n                switch (p.getType().getScalarType().orElse(null)) {\n                  case BOOLEAN -> ((Boolean) p.getValue(index)) ? 1.0f : 0.0f;\n                  case UINT_8 -> ((U8) p.getValue(index)).intValue().floatValue();\n                  case UINT_32 -> ((U32) p.getValue(index)).longValue().floatValue();\n                  case UINT_64 -> ((U64) p.getValue(index)).longValue().floatValue();\n                  case INT_8, INT_32, INT_64, FLOAT -> ((Number) p.getValue(index)).floatValue();\n                  case DOUBLE -> strictFloatOrNull((Double) p.getValue(index));\n                  default -> null;\n                })\n        .orElse(null);\n  }\n\n  private static Double getDoublePropertyValue(@NotNull Feature feature, @NotNull String name) {\n    final var index = feature.getIndex();\n    return feature\n        .findProperty(name)\n        .map(\n            p ->\n                switch (p.getType().getScalarType().orElse(null)) {\n                  case BOOLEAN -> ((Boolean) p.getValue(index)) ? 1.0 : 0.0;\n                  case UINT_8 -> ((U8) p.getValue(index)).intValue().doubleValue();\n                  case UINT_32 -> ((U32) p.getValue(index)).longValue().doubleValue();\n                  case UINT_64 -> ((U64) p.getValue(index)).longValue().doubleValue();\n                  case INT_8, INT_32, INT_64, FLOAT, DOUBLE ->\n                      ((Number) p.getValue(index)).doubleValue();\n                  default -> null;\n                })\n        .orElse(null);\n  }\n\n  private static String getStringPropertyValue(\n      @NotNull Feature feature, @NotNull String name, boolean coercePropertyValues) {\n    final var index = feature.getIndex();\n    return feature\n        .findProperty(name)\n        .map(\n            p ->\n                switch (p.getType().getScalarType().orElse(null)) {\n                  case STRING -> (String) p.getValue(index);\n                  default -> coercePropertyValues ? p.getValue(index).toString() : null;\n                })\n        .orElse(null);\n  }\n\n  public static ArrayList<byte[]> encodeScalarPropertyColumn(\n      MltMetadata.Column columnMetadata,\n      boolean isID,\n      SequencedCollection<Feature> features,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean useFSST,\n      boolean coercePropertyValues,\n      @NotNull ConversionConfig.IntegerEncodingOption integerEncodingOption)\n      throws IOException {\n    final var scalarType =\n        columnMetadata\n            .getScalarType()\n            .orElseThrow(() -> new IllegalArgumentException(\"scalarType must not be null\"));\n    return switch (scalarType) {\n      case BOOLEAN ->\n          // no stream count\n          encodeBooleanColumn(features, columnMetadata);\n      case INT_32, UINT_32 -> {\n        final var signed = (scalarType == MltMetadata.ScalarType.INT_32);\n        // no stream count\n        yield encodeInt32Column(\n            features, columnMetadata, isID, physicalLevelTechnique, signed, integerEncodingOption);\n      }\n      case INT_64, UINT_64 -> {\n        final var signed = (scalarType == MltMetadata.ScalarType.INT_64);\n        // no stream count\n        yield encodeInt64Column(features, columnMetadata, isID, signed, integerEncodingOption);\n      }\n      case FLOAT -> {\n        // no stream count\n        yield encodeFloatColumn(features, columnMetadata);\n      }\n      case DOUBLE -> {\n        // no stream count\n        yield encodeDoubleColumn(features, columnMetadata);\n      }\n      case STRING ->\n          encodeStringColumn(\n              columnMetadata, features, physicalLevelTechnique, useFSST, coercePropertyValues);\n      default ->\n          throw new IllegalArgumentException(\n              \"The specified scalar data type is currently not supported: \" + scalarType);\n    };\n  }\n\n  private static ArrayList<byte[]> encodeStringColumn(\n      MltMetadata.Column columnMetadata,\n      SequencedCollection<Feature> features,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean useFSST,\n      boolean coercePropertyValues)\n      throws IOException {\n    /*\n     * -> Single Column\n     *   -> Plain Encoding Stream -> present, length, data\n     *   -> Dictionary Encoding Streams -> present, length, data, dictionary\n     * -> N Columns Dictionary\n     *   -> SharedDictionaryLength, SharedDictionary, present1, data1, present2, data2\n     * -> N Columns FsstDictionary\n     * */\n    final var rawStringValues =\n        features.stream()\n            .map(f -> getStringPropertyValue(f, columnMetadata.getName(), coercePropertyValues))\n            .toArray(String[]::new);\n    final var stringValues = Arrays.stream(rawStringValues).filter(Objects::nonNull).toList();\n\n    final ArrayList<byte[]> presentStream;\n    if (columnMetadata.isNullable()) {\n      final var presentValues =\n          Arrays.stream(rawStringValues).map(Objects::nonNull).toArray(Boolean[]::new);\n      presentStream = BooleanEncoder.encodeBooleanStream(presentValues, PhysicalStreamType.PRESENT);\n    } else {\n      presentStream = new ArrayList<>();\n    }\n\n    final var stringColumn = StringEncoder.encode(stringValues, physicalLevelTechnique, useFSST);\n\n    /* Plus 1 for present stream */\n    final var hasPresentStream = ByteArrayUtil.totalLength(presentStream) > 0;\n    final var streamCount = stringColumn.getLeft() + (hasPresentStream ? 1 : 0);\n    final var encodedFieldMetadata = EncodingUtils.encodeVarint(streamCount, false);\n\n    final var result =\n        new ArrayList<byte[]>(presentStream.size() + stringColumn.getRight().size() + 1);\n    result.add(encodedFieldMetadata);\n    result.addAll(presentStream);\n    result.addAll(stringColumn.getRight());\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeBooleanColumn(\n      SequencedCollection<Feature> features, MltMetadata.Column metadata) throws IOException {\n    final var presentStream = metadata.isNullable() ? new BitSet(features.size()) : null;\n    final var dataStream = new BitSet();\n    var dataStreamIndex = 0;\n    var presentStreamIndex = 0;\n    for (var feature : features) {\n      final var propertyValue = getBooleanPropertyValue(feature, metadata.getName());\n      final var present = (propertyValue != null);\n      if (present) {\n        dataStream.set(dataStreamIndex++, (boolean) propertyValue);\n      }\n      if (presentStream != null) {\n        presentStream.set(presentStreamIndex++, present);\n      }\n    }\n\n    final var encodedPresentStream =\n        (presentStream != null)\n            ? EncodingUtils.encodeBooleanRle(presentStream, presentStreamIndex)\n            : new byte[0];\n    final var encodedDataStream = EncodingUtils.encodeBooleanRle(dataStream, dataStreamIndex);\n\n    final var result =\n        (presentStream != null)\n            ? new StreamMetadata(\n                    PhysicalStreamType.PRESENT,\n                    null,\n                    LogicalLevelTechnique.RLE,\n                    LogicalLevelTechnique.NONE,\n                    PhysicalLevelTechnique.NONE,\n                    presentStreamIndex,\n                    encodedPresentStream.length)\n                .encode()\n            : new ArrayList<byte[]>();\n    final var encodedDataStreamMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                null,\n                LogicalLevelTechnique.RLE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                dataStreamIndex,\n                encodedDataStream.length)\n            .encode();\n\n    result.add(encodedPresentStream);\n    result.addAll(encodedDataStreamMetadata);\n    result.add(encodedDataStream);\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeFloatColumn(\n      SequencedCollection<Feature> features, MltMetadata.Column metadata) throws IOException {\n    final var values = new ArrayList<Float>(features.size());\n    final var presentValues = metadata.isNullable() ? new Boolean[features.size()] : null;\n    var presentIndex = 0;\n    for (var feature : features) {\n      final var propertyValue = getFloatPropertyValue(feature, metadata.getName());\n      final var present = (propertyValue != null);\n      if (present) {\n        values.add(propertyValue);\n      }\n      if (presentValues != null) {\n        presentValues[presentIndex++] = present;\n      }\n    }\n\n    final var encodedPresentStream =\n        (presentValues != null)\n            ? BooleanEncoder.encodeBooleanStream(presentValues, PhysicalStreamType.PRESENT)\n            : null;\n    final var result = FloatEncoder.encodeFloatStream(values);\n    if (encodedPresentStream != null) {\n      result.addAll(0, encodedPresentStream);\n    }\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeDoubleColumn(\n      SequencedCollection<Feature> features, MltMetadata.Column metadata) throws IOException {\n    final var values = new ArrayList<Double>(features.size());\n    final var presentValues = metadata.isNullable() ? new Boolean[features.size()] : null;\n    var presentIndex = 0;\n    for (var feature : features) {\n      final var propertyValue = getDoublePropertyValue(feature, metadata.getName());\n      final var present = (propertyValue != null);\n      if (present) {\n        values.add(propertyValue);\n      }\n      if (presentValues != null) {\n        presentValues[presentIndex++] = present;\n      }\n    }\n\n    final var encodedPresentStream =\n        (presentValues != null)\n            ? BooleanEncoder.encodeBooleanStream(presentValues, PhysicalStreamType.PRESENT)\n            : null;\n    final var result = DoubleEncoder.encodeDoubleStream(values);\n    if (encodedPresentStream != null) {\n      result.addAll(0, encodedPresentStream);\n    }\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeInt32Column(\n      SequencedCollection<Feature> features,\n      MltMetadata.Column metadata,\n      boolean isID,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean isSigned,\n      @NotNull ConversionConfig.IntegerEncodingOption integerEncodingOption)\n      throws IOException {\n    final var values = new ArrayList<Integer>(features.size());\n    final var presentValues = metadata.isNullable() ? new Boolean[features.size()] : null;\n    var presentIndex = 0;\n    for (var feature : features) {\n      // Force ID values to integer for this column.\n      // If long were required, `encodeInt64Column` would have been called instead.\n      final var propertyValue =\n          isID\n              ? (feature.hasId() ? Integer.valueOf(Math.toIntExact(feature.getId())) : null)\n              : getIntPropertyValue(feature, metadata.getName());\n      final var present = (propertyValue != null);\n      if (present) {\n        values.add(propertyValue);\n      }\n      if (presentValues != null) {\n        presentValues[presentIndex++] = present;\n      }\n      // If the column is not nullable, all values must be present.\n      // Failure of this assertion indicates a problem with metadata creation,\n      // or use of the metadata to encode data other than what it describes.\n      assert (present || metadata.isNullable());\n    }\n\n    final var encodedPresentStream =\n        (presentValues != null)\n            ? BooleanEncoder.encodeBooleanStream(presentValues, PhysicalStreamType.PRESENT)\n            : null;\n    final var result =\n        IntegerEncoder.encodeIntStream(\n            CollectionUtils.unboxInts(values),\n            physicalLevelTechnique,\n            isSigned,\n            PhysicalStreamType.DATA,\n            null,\n            integerEncodingOption);\n\n    if (encodedPresentStream != null) {\n      result.addAll(0, encodedPresentStream);\n    }\n    return result;\n  }\n\n  private static ArrayList<byte[]> encodeInt64Column(\n      SequencedCollection<Feature> features,\n      MltMetadata.Column metadata,\n      boolean isID,\n      boolean isSigned,\n      @NotNull ConversionConfig.IntegerEncodingOption integerEncodingOption)\n      throws IOException {\n    final var values = new ArrayList<Long>(features.size());\n    final var presentValues = metadata.isNullable() ? new Boolean[features.size()] : null;\n    var presentIndex = 0;\n    for (var feature : features) {\n      final var propertyValue =\n          isID ? feature.idOrNull() : getLongPropertyValue(feature, metadata.getName());\n      final var present = (propertyValue != null);\n      if (present) {\n        values.add(propertyValue);\n      }\n      if (presentValues != null) {\n        presentValues[presentIndex++] = present;\n      }\n    }\n\n    final var encodedPresentStream =\n        (presentValues != null)\n            ? BooleanEncoder.encodeBooleanStream(presentValues, PhysicalStreamType.PRESENT)\n            : null;\n    final var result =\n        IntegerEncoder.encodeLongStream(\n            CollectionUtils.unboxLongs(values),\n            isSigned,\n            PhysicalStreamType.DATA,\n            null,\n            integerEncodingOption);\n\n    if (encodedPresentStream != null) {\n      result.addAll(0, encodedPresentStream);\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/StringEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport org.apache.commons.lang3.mutable.MutableInt;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.maplibre.mlt.converter.encodings.fsst.FsstEncoder;\nimport org.maplibre.mlt.metadata.stream.DictionaryType;\nimport org.maplibre.mlt.metadata.stream.LengthType;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.LogicalStreamType;\nimport org.maplibre.mlt.metadata.stream.OffsetType;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\n\npublic class StringEncoder {\n\n  private StringEncoder() {}\n\n  /// Convert a collection of string columns into a shared dictionary encoded byte array\n  /// @return Pair of (number of streams, dictionary encoded to byte array)\n  public static Pair<Integer, ArrayList<byte[]>> encodeSharedDictionary(\n      List<List<String>> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean useFsstEncoding)\n      throws IOException {\n    /*\n     * compare single column encoding with shared dictionary encoding\n     * Shared dictionary layout -> length, dictionary, present1, data1, present2, data2\n     * Shared Fsst dictionary layout ->  symbol table, symbol length, dictionary/compressed corpus, length, present1, data1, present2, data2\n     * */\n    // TODO: also compare size with plain and single encoded columns\n    // TODO: also sort dictionary for the usage in combination with Gzip?\n    final var dictionary = new ArrayList<String>(values.size());\n    final var dictionarySize = new MutableInt(0);\n    final var dictMap = new HashMap<String, Integer>(values.size());\n    final var dataStreams = new ArrayList<List<Integer>>(values.size());\n    final var presentStreams = new ArrayList<Boolean[]>(values.size());\n    for (var column : values) {\n      final var presentStream = new Boolean[column.size()];\n      final var dataStream = new ArrayList<Integer>(column.size());\n      presentStreams.add(presentStream);\n      dataStreams.add(dataStream);\n\n      var presentIndex = 0;\n      for (var value : column) {\n        presentStream[presentIndex++] = (value != null);\n        if (value != null) {\n          dataStream.add(\n              dictMap.computeIfAbsent(\n                  value,\n                  v -> {\n                    final var index = dictionarySize.getAndIncrement();\n                    dictionary.add(v);\n                    return index;\n                  }));\n        }\n      }\n    }\n\n    if (dictionarySize.intValue() == 0) {\n      /* Set number of streams to zero if no columns are present in this tile */\n      return Pair.of(0, new ArrayList<>());\n    }\n\n    final var encodedSharedDictionary =\n        encodeDictionary(dictionary, physicalLevelTechnique, false, true);\n\n    final ArrayList<byte[]> encodedSharedFsstDictionary;\n    if (useFsstEncoding) {\n      encodedSharedFsstDictionary =\n          encodeFsstDictionary(\n              dictionary, dictionarySize.intValue(), physicalLevelTechnique, false);\n    } else {\n      encodedSharedFsstDictionary = null;\n    }\n\n    final var usingFSST =\n        useFsstEncoding\n            && ByteArrayUtil.totalLength(encodedSharedFsstDictionary)\n                < ByteArrayUtil.totalLength(encodedSharedDictionary);\n    final var result = usingFSST ? encodedSharedFsstDictionary : encodedSharedDictionary;\n\n    // stream_count = dict streams + actual streams written across all child columns.\n    // Plain has 2 dict streams (length + data).\n    // FSST has 4 dict streams (symbol_lengths, symbol_table, value_lengths, compressed_corpus).\n    // There used to be a bug here where one extra column was counted\n    var numStreams = usingFSST ? 4 : 2;\n\n    for (var i = 0; i < dataStreams.size(); i++) {\n      var presentStream = presentStreams.get(i);\n      var dataStream = dataStreams.get(i);\n\n      if (dataStream.isEmpty()) {\n        /* If no values are present in this column add zero for number of streams */\n        final var encodedFieldMetadata = EncodingUtils.encodeVarint(0, false);\n        result.add(encodedFieldMetadata);\n        continue;\n      }\n\n      final var encodedFieldMetadata = EncodingUtils.encodeVarints(new int[] {2}, false, false);\n\n      // TODO: make present stream optional\n      final var encodedPresentStream =\n          BooleanEncoder.encodeBooleanStream(presentStream, PhysicalStreamType.PRESENT);\n      numStreams += 2;\n\n      final var encodedDataStream =\n          IntegerEncoder.encodeIntStream(\n              dataStream,\n              physicalLevelTechnique,\n              false,\n              PhysicalStreamType.OFFSET,\n              new LogicalStreamType(OffsetType.STRING));\n\n      result.add(encodedFieldMetadata);\n      result.addAll(encodedPresentStream);\n      result.addAll(encodedDataStream);\n    }\n\n    return Pair.of(numStreams, result);\n  }\n\n  public static Pair<Integer, ArrayList<byte[]>> encode(\n      Collection<String> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean useFsstEncoding)\n      throws IOException {\n    /*\n     * convert a single string column -> check if plain, dictionary, fsst or fsstDictionary\n     * -> plain -> length, data\n     * -> dictionary -> length, dictionary, data\n     * -> fsst -> symbol length, symbol table, length, compressed corpus\n     * -> fsst dictionary -> symbol length, symbol table,  length, dictionary/compressed corpus, offset\n     * Schema selection\n     * -> based on statistics if dictionary encoding is used\n     * -> compare four possible encodings in size based on samples\n     * */\n    final var plainEncodedColumn = encodePlain(values, physicalLevelTechnique);\n\n    final var dictionaryEncodedColumn =\n        encodeDictionary(values, physicalLevelTechnique, true, false);\n\n    final ArrayList<byte[]> fsstEncodedDictionary;\n    if (useFsstEncoding) {\n      fsstEncodedDictionary =\n          encodeFsstDictionary(values, values.size(), physicalLevelTechnique, true);\n    } else {\n      fsstEncodedDictionary = null;\n    }\n\n    return Stream.of(\n            Pair.of(2, plainEncodedColumn),\n            Pair.of(3, dictionaryEncodedColumn),\n            Pair.of(5, fsstEncodedDictionary))\n        .filter(p -> p.getRight() != null)\n        .min(Comparator.comparingInt(a -> ByteArrayUtil.totalLength(a.getRight())))\n        .orElseThrow();\n  }\n\n  private static ArrayList<byte[]> encodeFsstDictionary(\n      Collection<String> values,\n      int valueCount,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean encodeDataStream)\n      throws IOException {\n    final var dataStream = new ArrayList<Integer>(valueCount);\n    final var dictionary = new ArrayList<String>(valueCount);\n    final var dictMap = new HashMap<String, Integer>(valueCount);\n    for (var value : values) {\n      dataStream.add(\n          dictMap.computeIfAbsent(\n              value,\n              v -> {\n                dictionary.add(v);\n                return dictionary.size() - 1;\n              }));\n    }\n\n    final var symbolTable = encodeFsst(dictionary, physicalLevelTechnique, false);\n\n    if (!encodeDataStream) {\n      return symbolTable;\n    }\n\n    final var encodedDataStream =\n        IntegerEncoder.encodeIntStream(\n            dataStream,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.OFFSET,\n            new LogicalStreamType(OffsetType.STRING));\n    symbolTable.addAll(encodedDataStream);\n    return symbolTable;\n  }\n\n  private static ArrayList<byte[]> encodeFsst(\n      Collection<String> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      @SuppressWarnings(\"SameParameterValue\") boolean isSharedDictionary)\n      throws IOException {\n    final var joinedValues = String.join(\"\", values).getBytes(StandardCharsets.UTF_8);\n    final var symbolTable = FsstEncoder.encode(joinedValues);\n\n    final var encodedSymbols = symbolTable.symbols();\n    final var compressedCorpus = symbolTable.compressedData();\n\n    final var symbolLengths =\n        Arrays.stream(symbolTable.symbolLengths()).boxed().collect(Collectors.toList());\n    final var encodedSymbolLengths =\n        IntegerEncoder.encodeIntStream(\n            symbolLengths,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.LENGTH,\n            new LogicalStreamType(LengthType.SYMBOL));\n    final var symbolTableMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                new LogicalStreamType(DictionaryType.FSST),\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                // TODO: numValues in this context not needed -> set 0 to save space -> only 1 byte\n                // with varint?\n                symbolLengths.size(),\n                encodedSymbols.length)\n            .encode();\n    final var lengthStream =\n        values.stream().mapToInt(v -> v.getBytes(StandardCharsets.UTF_8).length).toArray();\n    final var encodedLengthStream =\n        IntegerEncoder.encodeIntStream(\n            lengthStream,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.LENGTH,\n            new LogicalStreamType(LengthType.DICTIONARY));\n    final var compressedCorpusStreamMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                new LogicalStreamType(\n                    isSharedDictionary ? DictionaryType.SHARED : DictionaryType.SINGLE),\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                values.size(),\n                compressedCorpus.length)\n            .encode();\n\n    /* SymbolLength, SymbolTable, Value Length, Compressed Corpus */\n    final var result = encodedSymbolLengths;\n    result.addAll(symbolTableMetadata);\n    result.add(symbolTable.symbols());\n    result.addAll(encodedLengthStream);\n    result.addAll(compressedCorpusStreamMetadata);\n    result.add(compressedCorpus);\n    return result;\n  }\n\n  private static int totalLengthOf(Collection<String> values) {\n    return values.stream().mapToInt(String::length).sum();\n  }\n\n  private static ArrayList<byte[]> encodeDictionary(\n      Collection<String> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean encodeOffsetStream,\n      boolean isSharedDictionary)\n      throws IOException {\n    final var offsetStream = new ArrayList<Integer>(values.size());\n    final var lengthStream = new ArrayList<Integer>(values.size());\n    final var dictionary = new ArrayList<String>(values.size());\n    final var dictMap = new HashMap<String, Integer>(values.size());\n    final var dictionaryStream = new ByteArrayOutputStream(totalLengthOf(values));\n    for (var value : values) {\n      offsetStream.add(\n          dictMap.computeIfAbsent(\n              value,\n              v -> {\n                dictionary.add(v);\n                final var utf8EncodedData = value.getBytes(StandardCharsets.UTF_8);\n                lengthStream.add(utf8EncodedData.length);\n                try {\n                  dictionaryStream.write(utf8EncodedData);\n                } catch (IOException ex) {\n                  throw new UncheckedIOException(ex);\n                }\n                return dictionary.size() - 1;\n              }));\n    }\n    final var dictionaryData = dictionaryStream.toByteArray();\n\n    final var encodedLengthStream =\n        IntegerEncoder.encodeIntStream(\n            lengthStream,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.LENGTH,\n            new LogicalStreamType(LengthType.DICTIONARY));\n    final var encodedDictionaryStreamMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                new LogicalStreamType(\n                    isSharedDictionary ? DictionaryType.SHARED : DictionaryType.SINGLE),\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                dictionary.size(),\n                dictionaryData.length)\n            .encode();\n\n    if (!encodeOffsetStream) {\n      final var result = encodedLengthStream;\n      result.addAll(encodedDictionaryStreamMetadata);\n      result.add(dictionaryData);\n      return result;\n    }\n\n    final var encodedOffsetStream =\n        IntegerEncoder.encodeIntStream(\n            offsetStream,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.OFFSET,\n            new LogicalStreamType(OffsetType.STRING));\n    /* Length, Offset (String), Data (Dictionary -> Single) */\n    final var result = encodedLengthStream;\n    result.addAll(encodedOffsetStream);\n    result.addAll(encodedDictionaryStreamMetadata);\n    result.add(dictionaryData);\n    return result;\n  }\n\n  public static ArrayList<byte[]> encodePlain(\n      Collection<String> values, PhysicalLevelTechnique physicalLevelTechnique) throws IOException {\n    final var lengthStream = new ArrayList<Integer>(values.size());\n    final var dataStream = new ByteArrayOutputStream(totalLengthOf(values));\n    for (var value : values) {\n      final var utf8EncodedValue = value.getBytes(StandardCharsets.UTF_8);\n      dataStream.write(utf8EncodedValue);\n      lengthStream.add(utf8EncodedValue.length);\n    }\n    final var stringData = dataStream.toByteArray();\n\n    final var encodedLengthStream =\n        IntegerEncoder.encodeIntStream(\n            lengthStream,\n            physicalLevelTechnique,\n            false,\n            PhysicalStreamType.LENGTH,\n            new LogicalStreamType(LengthType.VAR_BINARY));\n\n    final var dataStreamMetadata =\n        new StreamMetadata(\n                PhysicalStreamType.DATA,\n                new LogicalStreamType(DictionaryType.NONE),\n                LogicalLevelTechnique.NONE,\n                LogicalLevelTechnique.NONE,\n                PhysicalLevelTechnique.NONE,\n                values.size(),\n                stringData.length)\n            .encode();\n\n    final var result = encodedLengthStream;\n    result.addAll(dataStreamMetadata);\n    result.add(stringData);\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/Fsst.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.io.ByteArrayOutputStream;\n\npublic interface Fsst {\n\n  SymbolTable encode(byte[] data);\n\n  default byte[] decode(SymbolTable encoded) {\n    return decode(\n        encoded.symbols(),\n        encoded.symbolLengths(),\n        encoded.compressedData(),\n        encoded.decompressedLength());\n  }\n\n  default byte[] decode(\n      byte[] symbols, int[] symbolLengths, byte[] compressedData, int decompressedLength) {\n    // optimized decoder that knows the output size so pre-allocates the array to avoid dynamically\n    // allocating a ByteArrayOutputStream\n    int idx = 0;\n    byte[] output = new byte[decompressedLength];\n\n    int[] symbolOffsets = new int[symbolLengths.length];\n    for (int i = 1; i < symbolLengths.length; i++) {\n      symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];\n    }\n\n    for (int i = 0; i < compressedData.length; i++) {\n      // In Java a byte[] is signed [-128 to 127], whereas in C++ it is unsigned [0 to 255]\n      // So we do a bit shifting operation to convert the values into unsigned values for easier\n      // handling\n      int symbolIndex = compressedData[i] & 0xFF;\n      // 255 is our escape byte -> take the next symbol as it is\n      if (symbolIndex == 255) {\n        output[idx++] = compressedData[++i];\n      } else if (symbolIndex < symbolLengths.length) {\n        int len = symbolLengths[symbolIndex];\n        System.arraycopy(symbols, symbolOffsets[symbolIndex], output, idx, len);\n        idx += len;\n      }\n    }\n\n    return output;\n  }\n\n  /**\n   * @deprecated use {@link #decode(byte[], int[], byte[], int)} instead with an explicit length\n   */\n  @Deprecated\n  default byte[] decode(byte[] symbols, int[] symbolLengths, byte[] compressedData) {\n    ByteArrayOutputStream decodedData = new ByteArrayOutputStream();\n\n    int[] symbolOffsets = new int[symbolLengths.length];\n    for (int i = 1; i < symbolLengths.length; i++) {\n      symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];\n    }\n\n    for (int i = 0; i < compressedData.length; i++) {\n      // In Java a byte[] is signed [-128 to 127], whereas in C++ it is unsigned [0 to 255]\n      // So we do a bit shifting operation to convert the values into unsigned values for easier\n      // handling\n      int symbolIndex = compressedData[i] & 0xFF;\n      // 255 is our escape byte -> take the next symbol as it is\n      if (symbolIndex == 255) {\n        decodedData.write(compressedData[++i] & 0xFF);\n      } else if (symbolIndex < symbolLengths.length) {\n        decodedData.write(symbols, symbolOffsets[symbolIndex], symbolLengths[symbolIndex]);\n      }\n    }\n\n    return decodedData.toByteArray();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/FsstDebug.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nclass FsstDebug implements Fsst {\n  private final Fsst java = new FsstJava();\n  private final Fsst jni = new FsstJni();\n\n  private static final AtomicLong jniTime = new AtomicLong();\n  private static final AtomicLong javaTime = new AtomicLong();\n  private static final AtomicLong jniSize = new AtomicLong();\n  private static final AtomicLong javaSize = new AtomicLong();\n  private static final AtomicInteger javaSmaller = new AtomicInteger();\n  private static final AtomicInteger jniSmaller = new AtomicInteger();\n  private static final AtomicBoolean printed = new AtomicBoolean(false);\n\n  static {\n    Runtime.getRuntime()\n        .addShutdownHook(\n            new Thread(\n                () -> {\n                  System.err.print(FsstDebug.printStatsOnce());\n                }));\n  }\n\n  @Override\n  public SymbolTable encode(byte[] data) {\n    final long a = System.currentTimeMillis();\n    final var fromJni = jni.encode(data);\n    final long b = System.currentTimeMillis();\n    final var fromJava = java.encode(data);\n    final long c = System.currentTimeMillis();\n    jniTime.addAndGet(b - a);\n    javaTime.addAndGet(c - b);\n    jniSize.addAndGet(fromJni.weight());\n    javaSize.addAndGet(fromJava.weight());\n    (fromJava.weight() <= fromJni.weight() ? javaSmaller : jniSmaller).incrementAndGet();\n    return fromJava;\n  }\n\n  public static String printStats() {\n    return new StringBuilder()\n        .append(\"java/jni:\")\n        .append(javaTime)\n        .append(\"ms/\")\n        .append(jniTime)\n        .append(\"ms \")\n        .append(javaSize)\n        .append(\"/\")\n        .append(jniSize)\n        .append(\" \")\n        .append(javaSize.get() * 1d / jniSize.get())\n        .append(\" jni smaller \")\n        .append(jniSmaller)\n        .append(\"/\")\n        .append(javaSmaller.get() + jniSmaller.get())\n        .append(\"\\n\")\n        .toString();\n  }\n\n  public static String printStatsOnce() {\n    return printed.getAndSet(true) ? \"\" : printStats();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/FsstEncoder.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\npublic class FsstEncoder {\n  public static Fsst INSTANCE;\n\n  public static boolean useNative(boolean value) {\n    if (value) {\n      if (!FsstJni.isLoaded()) {\n        INSTANCE = new FsstJni();\n        return true;\n      } else {\n        return false;\n      }\n    } else {\n      INSTANCE = new FsstJava();\n      return true;\n    }\n  }\n\n  private static Fsst getInstance() {\n    if (INSTANCE == null) {\n      useNative(false);\n    }\n    return INSTANCE;\n  }\n\n  private FsstEncoder() {}\n\n  public static SymbolTable encode(byte[] data) {\n    return getInstance().encode(data);\n  }\n\n  public static byte[] decode(\n      byte[] symbols, int[] symbolLengths, byte[] compressedData, int decompressedLength) {\n    return getInstance().decode(symbols, symbolLengths, compressedData, decompressedLength);\n  }\n\n  /**\n   * @deprecated use {@link #decode(byte[], int[], byte[], int)} instead with an explicit length\n   */\n  @Deprecated\n  public static byte[] decode(byte[] symbols, int[] symbolLengths, byte[] compressedData) {\n    return getInstance().decode(symbols, symbolLengths, compressedData);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/FsstJava.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nclass FsstJava implements Fsst {\n\n  @Override\n  public SymbolTable encode(byte[] data) {\n    return SymbolTableBuilder.encode(data);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/FsstJni.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.nio.file.FileSystems;\n\npublic class FsstJni implements Fsst {\n\n  private static boolean isLoaded = false;\n  private static UnsatisfiedLinkError loadError;\n\n  static {\n    String os = System.getProperty(\"os.name\").toLowerCase();\n    boolean isWindows = os.contains(\"win\");\n    String moduleDir = \"build/FsstWrapper.so\";\n    if (isWindows) {\n      // TODO: figure out how to get cmake to put in common directory\n      moduleDir = \"build/Release/FsstWrapper.so\";\n    }\n    String modulePath =\n        FileSystems.getDefault().getPath(moduleDir).normalize().toAbsolutePath().toString();\n    try {\n      System.load(modulePath);\n      isLoaded = true;\n    } catch (UnsatisfiedLinkError e) {\n      loadError = e;\n    }\n  }\n\n  public SymbolTable encode(byte[] data) {\n    return FsstJni.compress(data);\n  }\n\n  public static boolean isLoaded() {\n    return isLoaded;\n  }\n\n  public static UnsatisfiedLinkError getLoadError() {\n    return loadError;\n  }\n\n  public static native SymbolTable compress(byte[] inputBytes);\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/Symbol.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n/**\n * A candidate symbol used when building the FSST table.\n *\n * <p>The first pass of FSST encoding builds single-byte symbols found in the data to encode, and\n * each subsequent pass combines those symbols into larger ones.\n */\nclass Symbol implements Comparable<Symbol> {\n  private final byte[] bytes;\n  // performance optimizations: precompute hashcode and store length to save an extra dereference\n  private final int length;\n  private final int hashcode;\n\n  private Symbol(byte[] bytes, int hashcode) {\n    this.bytes = bytes;\n    this.length = bytes.length;\n    this.hashcode = hashcode;\n  }\n\n  public static Symbol of(int b) {\n    return new Symbol(new byte[] {(byte) b}, 31 + (byte) b);\n  }\n\n  public static Symbol concat(Symbol a, Symbol b) {\n    int len = Math.min(SymbolTableBuilder.MAX_SYMBOL_LENGTH, a.length + b.length);\n    byte[] result = new byte[len];\n    System.arraycopy(a.bytes, 0, result, 0, a.length);\n    int todo = len - a.length;\n    if (todo > 0) System.arraycopy(b.bytes, 0, result, a.length, todo);\n    int hash = a.hashcode;\n    for (byte bb : b.bytes) {\n      hash = 31 * hash + bb;\n    }\n    return new Symbol(result, hash);\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    return (this == o) || (o instanceof Symbol other && Arrays.equals(bytes, other.bytes));\n  }\n\n  @Override\n  public int hashCode() {\n    return hashcode;\n  }\n\n  public int length() {\n    return length;\n  }\n\n  public byte[] bytes() {\n    return bytes;\n  }\n\n  @Override\n  public int compareTo(Symbol o) {\n    // sort symbols lexicographically except longer symbols are before symbols that are their prefix\n    if (this == o) return 0;\n    var a = bytes;\n    var b = o.bytes;\n\n    int i = Arrays.mismatch(a, b);\n    if (i >= 0 && i < a.length && i < b.length) {\n      return Byte.compareUnsigned(a[i], b[i]);\n    }\n\n    // only difference from Arrays.compareUnsigned - when one is a prefix\n    // of the other, sort the longest one first\n    return b.length - a.length;\n  }\n\n  public int first() {\n    return bytes[0] & 0xFF;\n  }\n\n  /**\n   * Returns true if this symbol appears at index {@code offset} in {@code text}, skipping the first\n   * {@code ignore} bytes if we already know they match.\n   */\n  public boolean match(ByteBuffer text, int offset, int ignore) {\n    if (text.capacity() < offset + length) {\n      return false;\n    }\n    // optimization: when symbols are indexed by first 1 or 2 bytes, we already know those bytes\n    // match so don't need to check them\n    for (int i = ignore; i < length; i++) {\n      if (text.get(offset + i) != bytes[i]) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return \"Symbol[\" + new String(bytes, StandardCharsets.UTF_8) + \"]\";\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/SymbolTable.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport java.util.Arrays;\n\n/**\n * Output of FSST-encoding: a symbol table, and text encoded with that symbol table.\n *\n * @param symbols Up to 255 symbols in the table concatenated together\n * @param symbolLengths Lengths of each of those 255 symbols in the table\n * @param compressedData Encoded text where a value less than 255 refers to that symbol from the\n *     table, and a value of 255 means the next byte should be used directly.\n * @param decompressedLength The length of the text after decompression\n */\npublic record SymbolTable(\n    byte[] symbols, int[] symbolLengths, byte[] compressedData, int decompressedLength) {\n  @Override\n  public String toString() {\n    return \"SymbolTable{weight=\"\n        + weight()\n        + \", symbols=byte[\"\n        + symbols.length\n        + \"], symbolLengths=int[\"\n        + symbolLengths.length\n        + \"], compressedData=byte[\"\n        + compressedData.length\n        + \"], decompressedLength=\"\n        + decompressedLength\n        + '}';\n  }\n\n  public int weight() {\n    return symbols.length + symbolLengths.length + compressedData.length;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    return this == o\n        || (o instanceof SymbolTable that\n            && Arrays.equals(symbols, that.symbols)\n            && Arrays.equals(symbolLengths, that.symbolLengths)\n            && Arrays.equals(compressedData, that.compressedData));\n  }\n\n  @Override\n  public int hashCode() {\n    int result = Arrays.hashCode(symbols);\n    result = 31 * result + Arrays.hashCode(symbolLengths);\n    result = 31 * result + Arrays.hashCode(compressedData);\n    return result;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/encodings/fsst/SymbolTableBuilder.java",
    "content": "/*\nMIT License\n\nCopyright (c) 2018-2020, CWI, TU Munich, FSU Jena\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n */\n\npackage org.maplibre.mlt.converter.encodings.fsst;\n\nimport com.carrotsearch.hppc.ByteArrayList;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.PriorityQueue;\nimport java.util.stream.IntStream;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Port of the FSST-encoding algorithm from <a\n * href=\"https://github.com/cwida/fsst\">github.com/cwida/fsst</a>.\n *\n * <p>Many of the C++ specific optimizations are excluded or replaced with the simpler python logic\n * described in <a\n * href=\"https://github.com/cwida/fsst/blob/master/fsstcompression.pdf\">fsstcompression.pdf</a> to\n * improve readability.\n *\n * <p>Some Java-specific optimizations are added to bring performance within 50% of the C++ version\n * on real-world data.\n */\nclass SymbolTableBuilder {\n  static final int MAX_SYMBOL_LENGTH = 8;\n  private static final int NUM_ITERS = 6;\n  public static final int DEFAULT_SAMPLE_SIZE = 30_000;\n  private final int sampleSize;\n  private final Symbol[] symbols = new Symbol[512];\n\n  /** Index single-byte symbol in symbols array by their only byte. */\n  private final int[] sIndex = new int[256];\n\n  /** Index multi-byte symbols in symbols array by their first 2 bytes. */\n  private final int[] sIndexByFirst2 = new int[(1 << 16) + 1];\n\n  /**\n   * Map from index in {@link #symbols} to a new index that sorts symbols by length ascending with\n   * single-byte symbols last\n   */\n  private int[] sIndexByLength;\n\n  /** Map from index sorted by length to original index in {@link #symbols} */\n  private int[] sIndexByLengthReverse;\n\n  private int nSymbols;\n\n  private SymbolTableBuilder(int sampleSize) {\n    for (int code = 0; code < 256; code++) {\n      symbols[code] = Symbol.of(code);\n    }\n    this.sampleSize = sampleSize;\n  }\n\n  /** Builds a symbol table with up to a 30kb sample and compresses {@code data} with it. */\n  public static SymbolTable encode(byte[] data) {\n    var buf = ByteBuffer.wrap(data);\n    return buildSymbolTable(buf, DEFAULT_SAMPLE_SIZE).encode(buf);\n  }\n\n  public static SymbolTableBuilder buildSymbolTable(ByteBuffer data, int sampleSize) {\n    // main loop: init symbol table with single-byte symbols...\n    SymbolTableBuilder st = new SymbolTableBuilder(sampleSize);\n    SymbolTableBuilder bestTable = st;\n    long bestWeight = Long.MAX_VALUE;\n    Counters counters;\n    Counters bestCounters = null;\n    for (int i = 1; i <= NUM_ITERS; i++) {\n      // then gather statistics about symbol frequencies and encoded data size\n      counters = new Counters();\n      long weight = st.compressCount(counters, data, i < NUM_ITERS);\n      if (weight <= bestWeight) {\n        bestCounters = counters;\n        bestTable = st;\n        bestWeight = weight;\n      }\n      // and iteratively combine symbols and return the best one\n      if (i < NUM_ITERS) st = st.makeTable(counters, false, sampleSize < data.capacity());\n    }\n\n    var result = bestTable.makeTable(bestCounters, true, sampleSize < data.capacity());\n    return result.sortSymbolsByLength();\n  }\n\n  private SymbolTableBuilder sortSymbolsByLength() {\n    // sort symbols by length ascending, with length 1 symbols last\n    sIndexByLength = new int[nSymbols];\n    sIndexByLengthReverse = new int[nSymbols];\n    int idx = 0;\n    // 2, 3, 4, 5, 6, 7, 8, 1\n    for (int b = 2; b <= MAX_SYMBOL_LENGTH + 1; b++) {\n      int len = b > MAX_SYMBOL_LENGTH ? 1 : b;\n      for (int i = 0; i < nSymbols; i++) {\n        var symbol = symbols[256 + i];\n        if (symbol.length() == len) {\n          int j = idx++;\n          sIndexByLength[i] = j;\n          sIndexByLengthReverse[j] = i;\n        }\n      }\n    }\n    return this;\n  }\n\n  private SymbolTable encode(ByteBuffer text) {\n    var encodedSymbols = new ByteArrayList();\n    int[] lengths = new int[nSymbols];\n    for (int i = 0; i < nSymbols; i++) {\n      var symbol = this.symbols[256 + sIndexByLengthReverse[i]];\n      lengths[i] = symbol.length();\n      encodedSymbols.add(symbol.bytes());\n    }\n    byte[] encodedText = encodeText(text, lengths);\n\n    return new SymbolTable(encodedSymbols.toArray(), lengths, encodedText, text.capacity());\n  }\n\n  private byte[] encodeText(ByteBuffer text, int[] lens) {\n    int cap = text.capacity();\n    var encoded = new ByteArrayList(cap);\n    for (int i = 0; i < cap; ) {\n      int code = findLongestSymbol(text, i);\n      if (isEscapeCode(code)) {\n        encoded.add((byte) 255, text.get(i++));\n      } else {\n        int symbol = sIndexByLength[code - 256];\n        encoded.add((byte) symbol);\n        i += lens[symbol];\n      }\n    }\n    return encoded.toArray();\n  }\n\n  private static boolean isEscapeCode(int code) {\n    return code < 256;\n  }\n\n  private static void addOrInc(Map<Symbol, Long> cands, Symbol s, long count, int min) {\n    if (count >= min) {\n      long gain = count * s.length();\n      cands.merge(s, gain, Long::sum);\n    }\n  }\n\n  private void add(Symbol symbol) {\n    symbols[256 + (nSymbols++)] = symbol;\n  }\n\n  private int findLongestSymbol(ByteBuffer text, int offset) {\n    // first look for a multi-byte symbol starting with the next 2 bytes\n    if (text.capacity() - offset >= 2) {\n      int a = text.getShort(offset) & 0xFFFF;\n      int start = sIndexByFirst2[a];\n      if (start > 0) {\n        int end = sIndexByFirst2[a + 1];\n        for (int code = start; code < end; code++) {\n          if (symbols[code].match(text, offset, 2)) {\n            return code;\n          }\n        }\n      }\n    }\n\n    // if not found, then look for a single-byte symbol\n    var letter = text.get(offset) & 0xFF;\n    int code = sIndex[letter];\n\n    if (!isEscapeCode(code)) {\n      return code;\n    }\n\n    // otherwise just return the \"escape code\" for this symbol since it's not in the table\n    return letter;\n  }\n\n  record Range(int start, int end) {}\n\n  private List<Range> ranges(int size) {\n    if (size < sampleSize) {\n      return List.of(new Range(0, size));\n    } else {\n      int chunkSize = 1000;\n      int samples = sampleSize / chunkSize;\n      int offset = size / (samples);\n      return IntStream.range(0, samples)\n          .mapToObj(i -> new Range(i * offset, Math.min(size, i * offset + chunkSize)))\n          .toList();\n    }\n  }\n\n  private long compressCount(Counters counters, ByteBuffer text, boolean secondPass) {\n    if (text.capacity() == 0) return 0;\n    long weight = 0;\n\n    for (var range : ranges(text.capacity())) {\n      int start = range.start;\n      int end = range.end;\n      int code2;\n      int code1 = findLongestSymbol(text, start);\n      Symbol symbol = symbols[code1];\n      int cur = start + symbol.length();\n      start = cur;\n      weight += isEscapeCode(code1) ? 2 : 1;\n      while (cur < end) {\n        // count single symbol (i.e. an option is not extending it)\n        counters.count1Inc(code1);\n        // as an alternative, consider just using the next byte..\n        if (symbol.length() > 1) { // .. but do not count single byte symbols doubly\n          counters.count1Inc(text.get(start) & 0xFF);\n        }\n\n        // now match a new symbol\n        start = cur;\n        code2 = findLongestSymbol(text, cur);\n        Symbol symbol2 = symbols[code2];\n        cur += symbol2.length();\n        weight += isEscapeCode(code2) ? 2 : 1;\n        if (secondPass) { // no need to count pairs in final round\n          // consider the symbol that is the concatenation of the two last symbols\n          counters.count2Inc(code1, code2);\n          // as an alternative, consider just extending with the next byte..\n          if (symbol2.length() > 1) { // ..but do not count single byte extensions doubly\n            counters.count2Inc(code1, text.get(start) & 0xFF);\n          }\n        }\n        code1 = code2;\n        symbol = symbols[code1];\n      }\n    }\n    // account for the encoded symbol table size\n    for (int i = 0; i < nSymbols; i++) {\n      weight += symbols[256 + i].length() + 1;\n    }\n    return weight;\n  }\n\n  private SymbolTableBuilder makeTable(Counters counters, boolean lastPass, boolean sampled) {\n    int minCount = 5;\n    // hashmap of c (needed because we can generate duplicate candidates)\n    Map<Symbol, Long> cands = new HashMap<>();\n    int max = 256 + nSymbols;\n    for (int pos1 = 0; pos1 < max; pos1++) {\n      int cnt1 = counters.count1GetNext(pos1);\n      if (cnt1 <= 0) continue;\n      Symbol s1 = symbols[pos1];\n      // note from C++ implementation:\n      // heuristic: promoting single-byte symbols (*8) helps reduce exception rates and increases\n      // [de]compression speed\n      addOrInc(\n          cands, s1, (s1.length() == 1 ? 8L : 1L) * cnt1, (lastPass && !sampled) ? 1 : minCount);\n\n      // don't need pair-wise counts for last pass to just encode the data\n      if (lastPass || s1.length() == MAX_SYMBOL_LENGTH) continue;\n      for (int pos2 = 0; pos2 < max; pos2++) {\n        int cnt2 = counters.count2GetNext(pos1, pos2);\n        if (cnt2 < minCount) continue;\n        addOrInc(cands, Symbol.concat(s1, symbols[pos2]), cnt2, minCount);\n      }\n    }\n\n    PriorityQueue<QSymbol> pq = new PriorityQueue<>();\n    for (var entry : cands.entrySet()) {\n      pq.add(new QSymbol(entry.getValue(), entry.getKey()));\n    }\n    SymbolTableBuilder st = new SymbolTableBuilder(sampleSize);\n    while (st.nSymbols < 255 && !pq.isEmpty()) {\n      var symb = pq.remove();\n      if (!lastPass || sampled) {\n        st.add(symb.symbol);\n      } else {\n        // adding a symbol costs length + 1, so don't add if it costs more than it saves\n        long costs = symb.symbol.length() + 1L;\n        long saves = symb.symbol.length() == 1 ? symb.gain / 8 : symb.gain;\n        if (saves > costs) {\n          st.add(symb.symbol);\n        }\n      }\n    }\n\n    return st.finish();\n  }\n\n  public SymbolTableBuilder finish() {\n    Symbol[] tmp = Arrays.copyOfRange(symbols, 256, 256 + nSymbols);\n    Arrays.sort(tmp); // sorts prefix symbols after the longer symbols\n    for (int i = nSymbols - 1; i >= 0; i--) {\n      int letter = tmp[i].first();\n      // index multi-byte by their first 2 bytes\n      if (tmp[i].length() >= 2) {\n        var bytes = tmp[i].bytes();\n        int val = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);\n        sIndexByFirst2[val] = 256 + i;\n        // there might be symbols with this prefix, so store end of the range by setting val+1\n        if (sIndexByFirst2[val + 1] == 0) {\n          sIndexByFirst2[val + 1] = 256 + i + 1;\n        }\n      } else {\n        // index single-byte symbols by their only byte\n        sIndex[letter] = 256 + i;\n      }\n      symbols[256 + i] = tmp[i];\n    }\n    return this;\n  }\n\n  private record QSymbol(long gain, Symbol symbol) implements Comparable<QSymbol> {\n    @Override\n    public int compareTo(@NotNull SymbolTableBuilder.QSymbol o) {\n      return Long.compare(o.gain, gain);\n    }\n  }\n\n  static class Counters {\n    private final int[] count1 = new int[512];\n    private final int[] count2 = new int[512 * 512];\n\n    public void count1Inc(int pos1) {\n      count1[pos1]++;\n    }\n\n    public void count2Inc(int pos1, int pos2) {\n      count2[(pos1 << 9) | pos2]++;\n    }\n\n    public int count1GetNext(int pos1) {\n      return count1[pos1];\n    }\n\n    public int count2GetNext(int pos1, int pos2) {\n      return count2[(pos1 << 9) | pos2];\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/GeometryType.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\npublic enum GeometryType {\n  POINT,\n  LINESTRING,\n  POLYGON,\n  MULTIPOINT,\n  MULTILINESTRING,\n  MULTIPOLYGON\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/GeometryUtils.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\nimport com.google.common.collect.Lists;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.TreeMap;\nimport java.util.stream.IntStream;\nimport org.apache.commons.lang3.tuple.Triple;\n\npublic class GeometryUtils {\n\n  private GeometryUtils() {}\n\n  public static void sortVertexOffsets(\n      List<Integer> numParts, int[] mortonEncodedDictionaryOffsetsIn, List<Long> featureIds) {\n    List<Integer> mortonEncodedDictionaryOffsets =\n        IntStream.of(mortonEncodedDictionaryOffsetsIn).boxed().toList();\n    // TODO: use an different proper optimization approach\n    /*\n     * Quick and dirty approach to sort the VertexOffsets of a VertexBuffer to reduce the deltas\n     * and therefore the overall size.\n     * The offsets are sorted based on the morton code of the first  vertex of a LineString.\n     * The order of the offsets of a LineString has to be preserved.\n     * */\n    var sortedDictionaryOffsets =\n        new TreeMap<Integer, Triple<List<Long>, List<Integer>, List<Integer>>>();\n    var partOffsetCounter = 0;\n    var idCounter = 0;\n    for (var numPart : numParts) {\n      var currentLineVertexOffsets =\n          mortonEncodedDictionaryOffsets.subList(partOffsetCounter, partOffsetCounter + numPart);\n      partOffsetCounter += numPart;\n\n      var featureId = featureIds.get(idCounter++);\n      var minVertexOffset = currentLineVertexOffsets.get(0);\n      if (sortedDictionaryOffsets.containsKey(minVertexOffset)) {\n        var sortedDictionaryOffset = sortedDictionaryOffsets.get(minVertexOffset);\n        sortedDictionaryOffset.getLeft().add(featureId);\n        sortedDictionaryOffset.getMiddle().addAll(currentLineVertexOffsets);\n        sortedDictionaryOffset.getRight().add(numPart);\n      } else {\n        sortedDictionaryOffsets.put(\n            minVertexOffset,\n            Triple.of(\n                Lists.newArrayList(featureId),\n                new ArrayList<>(currentLineVertexOffsets),\n                Lists.newArrayList(numPart)));\n      }\n    }\n\n    var sortedOffsets =\n        sortedDictionaryOffsets.values().stream().flatMap(e -> e.getMiddle().stream()).toList();\n    var updatedFeatureIds =\n        sortedDictionaryOffsets.values().stream().flatMap(e -> e.getLeft().stream()).toList();\n    var updatedNumParts =\n        sortedDictionaryOffsets.values().stream().flatMap(e -> e.getRight().stream()).toList();\n\n    int i = 0;\n    for (var morton : sortedOffsets) {\n      mortonEncodedDictionaryOffsetsIn[i++] = morton;\n    }\n    featureIds.clear();\n    featureIds.addAll(updatedFeatureIds);\n    numParts.clear();\n    numParts.addAll(updatedNumParts);\n  }\n\n  public static void sortPoints(\n      List<Vertex> points, HilbertCurve hilbertCurve, List<Long> featureIds) {\n    var sortedPoints = new ArrayList<Triple<Integer, Long, Vertex>>();\n    for (var i = 0; i < points.size(); i++) {\n      var featureId = featureIds.get(i);\n      var point = points.get(i);\n      var hilbertId = hilbertCurve.encode(point);\n      sortedPoints.add(Triple.of(hilbertId, featureId, point));\n    }\n\n    sortedPoints.sort(Comparator.comparingInt(Triple::getLeft));\n    featureIds.clear();\n    featureIds.addAll(sortedPoints.stream().map(Triple::getMiddle).toList());\n    points.clear();\n    points.addAll(sortedPoints.stream().map(Triple::getRight).toList());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/Hilbert.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\n/**\n * Fast hilbert curve calculations from <a\n * href=\"https://github.com/rawrunprotected/hilbert_curves\">https://github.com/rawrunprotected/hilbert_curves</a>\n * (public domain).\n */\npublic class Hilbert {\n  private Hilbert() {\n    throw new IllegalStateException(\"Utility class\");\n  }\n\n  private static int deinterleave(int tx) {\n    tx = tx & 0x55555555;\n    tx = (tx | (tx >>> 1)) & 0x33333333;\n    tx = (tx | (tx >>> 2)) & 0x0F0F0F0F;\n    tx = (tx | (tx >>> 4)) & 0x00FF00FF;\n    tx = (tx | (tx >>> 8)) & 0x0000FFFF;\n    return tx;\n  }\n\n  private static int interleave(int tx) {\n    tx = (tx | (tx << 8)) & 0x00FF00FF;\n    tx = (tx | (tx << 4)) & 0x0F0F0F0F;\n    tx = (tx | (tx << 2)) & 0x33333333;\n    tx = (tx | (tx << 1)) & 0x55555555;\n    return tx;\n  }\n\n  private static int prefixScan(int tx) {\n    tx = (tx >>> 8) ^ tx;\n    tx = (tx >>> 4) ^ tx;\n    tx = (tx >>> 2) ^ tx;\n    tx = (tx >>> 1) ^ tx;\n    return tx;\n  }\n\n  /**\n   * Returns the x/y coordinates from hilbert index {@code pos} at {@code level} packed into {x, y}\n   * coordinates.\n   */\n  public static int[] hilbertPositionToXY(int level, int pos) {\n    pos = pos << (32 - 2 * level);\n\n    int i0 = deinterleave(pos);\n    int i1 = deinterleave(pos >>> 1);\n\n    int t0 = (i0 | i1) ^ 0xFFFF;\n    int t1 = i0 & i1;\n\n    int prefixT0 = prefixScan(t0);\n    int prefixT1 = prefixScan(t1);\n\n    int a = (((i0 ^ 0xFFFF) & prefixT1) | (i0 & prefixT0));\n\n    int resultX = (a ^ i1) >>> (16 - level);\n    int resultY = (a ^ i0 ^ i1) >>> (16 - level);\n    return new int[] {resultX, resultY};\n  }\n\n  /** Returns the hilbert index at {@code level} for an x/y coordinate. */\n  public static int hilbertXYToIndex(int level, int x, int y) {\n    x = x << (16 - level);\n    y = y << (16 - level);\n\n    int hA, hB, hC, hD;\n\n    int a1 = x ^ y;\n    int b1 = 0xFFFF ^ a1;\n    int c1 = 0xFFFF ^ (x | y);\n    int d1 = x & (y ^ 0xFFFF);\n\n    hA = a1 | (b1 >>> 1);\n    hB = (a1 >>> 1) ^ a1;\n\n    hC = ((c1 >>> 1) ^ (b1 & (d1 >>> 1))) ^ c1;\n    hD = ((a1 & (c1 >>> 1)) ^ (d1 >>> 1)) ^ d1;\n\n    int a2 = hA;\n    int b2 = hB;\n    int c2 = hC;\n    int d2 = hD;\n\n    hA = ((a2 & (a2 >>> 2)) ^ (b2 & (b2 >>> 2)));\n    hB = ((a2 & (b2 >>> 2)) ^ (b2 & ((a2 ^ b2) >>> 2)));\n\n    hC ^= ((a2 & (c2 >>> 2)) ^ (b2 & (d2 >>> 2)));\n    hD ^= ((b2 & (c2 >>> 2)) ^ ((a2 ^ b2) & (d2 >>> 2)));\n\n    int a3 = hA;\n    int b3 = hB;\n    int c3 = hC;\n    int d3 = hD;\n\n    hA = ((a3 & (a3 >>> 4)) ^ (b3 & (b3 >>> 4)));\n    hB = ((a3 & (b3 >>> 4)) ^ (b3 & ((a3 ^ b3) >>> 4)));\n\n    hC ^= ((a3 & (c3 >>> 4)) ^ (b3 & (d3 >>> 4)));\n    hD ^= ((b3 & (c3 >>> 4)) ^ ((a3 ^ b3) & (d3 >>> 4)));\n\n    int a4 = hA;\n    int b4 = hB;\n    int c4 = hC;\n    int d4 = hD;\n\n    hC ^= ((a4 & (c4 >>> 8)) ^ (b4 & (d4 >>> 8)));\n    hD ^= ((b4 & (c4 >>> 8)) ^ ((a4 ^ b4) & (d4 >>> 8)));\n\n    int a = hC ^ (hC >>> 1);\n    int b = hD ^ (hD >>> 1);\n\n    int i0 = x ^ y;\n    int i1 = b | (0xFFFF ^ (i0 | a));\n\n    return ((interleave(i1) << 1) | interleave(i0)) >>> (32 - 2 * level);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/HilbertCurve.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\npublic class HilbertCurve extends SpaceFillingCurve {\n\n  public HilbertCurve(int minVertexValue, int maxVertexValue) {\n    super(minVertexValue, maxVertexValue);\n  }\n\n  public int encode(Vertex vertex) {\n    var shiftedX = vertex.x() + coordinateShift;\n    var shiftedY = vertex.y() + coordinateShift;\n    return Hilbert.hilbertXYToIndex(numBits, shiftedX, shiftedY);\n  }\n\n  public int[] decode(int hilbertIndex) {\n    return Hilbert.hilbertPositionToXY(numBits, hilbertIndex);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/SpaceFillingCurve.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\npublic abstract class SpaceFillingCurve {\n  protected int tileExtent;\n  protected int numBits;\n  protected int coordinateShift;\n  private final int minBound;\n  private final int maxBound;\n\n  public static final int MAX_SUPPORTED_BITS = 16;\n  public static final int MAX_SUPPORTED_RANGE = (1 << MAX_SUPPORTED_BITS) - 1;\n\n  public static boolean isRangeSupported(int minVertexValue, int maxVertexValue) {\n    return (maxVertexValue + getCoordinateShift(minVertexValue)) <= MAX_SUPPORTED_RANGE;\n  }\n\n  public SpaceFillingCurve(int minVertexValue, int maxVertexValue) {\n    // TODO: fix tile buffer problem\n    /* Each tile can have a buffer around, which means the coordinate values are extended beyond the specified tileExtent.\n     * Currently we are extending size tile size be half of to the size into each direction, which works well for the test tilesets.\n     * But this leads to problems if the tile coordinates are not within this boundaries.\n     * */\n    this.coordinateShift = getCoordinateShift(minVertexValue);\n    this.tileExtent = maxVertexValue + coordinateShift;\n    this.numBits = (int) Math.ceil((Math.log(this.tileExtent + 1) / Math.log(2)));\n    this.minBound = minVertexValue;\n    this.maxBound = maxVertexValue;\n\n    if (numBits > MAX_SUPPORTED_BITS) {\n      throw new IllegalArgumentException(\n          \"Tile coordinate span \" + this.tileExtent + \" is too large\");\n    }\n  }\n\n  protected void validateCoordinates(Vertex vertex) {\n    // TODO: also check of int overflow as we limiting the sfc ids to max int size\n    if (vertex.x() < minBound\n        || vertex.y() < minBound\n        || vertex.x() > maxBound\n        || vertex.y() > maxBound) {\n      throw new IllegalArgumentException(\n          \"The specified tile buffer size is currently not supported.\");\n    }\n  }\n\n  private static int getCoordinateShift(int minVertexValue) {\n    if (minVertexValue == Integer.MIN_VALUE) {\n      return Integer.MAX_VALUE;\n    }\n    return (minVertexValue < 0) ? Math.abs(minVertexValue) : 0;\n  }\n\n  public abstract int encode(Vertex vertex);\n\n  public abstract int[] decode(int mortonCode);\n\n  public int numBits() {\n    return this.numBits;\n  }\n\n  public int coordinateShift() {\n    return this.coordinateShift;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/Vertex.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\npublic record Vertex(int x, int y) {}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/geometry/ZOrderCurve.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\npublic class ZOrderCurve extends SpaceFillingCurve {\n\n  public ZOrderCurve(int minVertexValue, int maxVertexValue) {\n    super(minVertexValue, maxVertexValue);\n  }\n\n  public int encode(Vertex vertex) {\n    validateCoordinates(vertex);\n    var shiftedX = vertex.x() + coordinateShift;\n    var shiftedY = vertex.y() + coordinateShift;\n    int mortonCode = 0;\n    for (int i = 0; i < numBits; i++) {\n      mortonCode |= (shiftedX & (1 << i)) << i | (shiftedY & (1 << i)) << (i + 1);\n    }\n    return mortonCode;\n  }\n\n  public int[] decode(int mortonCode) {\n    int x = decodeMorton(mortonCode) - coordinateShift;\n    int y = decodeMorton(mortonCode >> 1) - coordinateShift;\n    return new int[] {x, y};\n  }\n\n  private int decodeMorton(int code) {\n    int coordinate = 0;\n    for (int i = 0; i < numBits; i++) {\n      coordinate |= (int) ((code & (1L << (2 * i))) >> i);\n    }\n    return coordinate;\n  }\n\n  public static int[] decode(int mortonCode, int numBits, int coordinateShift) {\n    int x = decodeMorton(mortonCode, numBits) - coordinateShift;\n    int y = decodeMorton(mortonCode >> 1, numBits) - coordinateShift;\n    return new int[] {x, y};\n  }\n\n  private static int decodeMorton(int code, int numBits) {\n    int coordinate = 0;\n    for (int i = 0; i < numBits; i++) {\n      coordinate |= (int) ((code & (1L << (2 * i))) >> i);\n    }\n    return coordinate;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/mvt/MvtUtils.java",
    "content": "package org.maplibre.mlt.converter.mvt;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\nimport no.ecc.vectortile.VectorTileDecoder;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MVTFeature;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.springmeyer.Pbf;\nimport org.springmeyer.VectorTile;\nimport org.springmeyer.VectorTileLayer;\n\npublic class MvtUtils {\n  /* Uses the java-vector-tile library for decoding the MVT tile */\n  public static MapboxVectorTile decodeMvt(Path mvtFilePath) throws IOException {\n    var mvt = Files.readAllBytes(mvtFilePath);\n    return decodeMvt(mvt);\n  }\n\n  /* Uses the java-vector-tile library for decoding the MVT tile */\n  public static List<VectorTileDecoder.Feature> decodeMvtFast(byte[] mvtTile) throws IOException {\n    VectorTileDecoder mvtDecoder = new VectorTileDecoder();\n    mvtDecoder.setAutoScale(false);\n    var decodedTile = mvtDecoder.decode(mvtTile);\n    return decodedTile.asList();\n  }\n\n  /* Use the java port of the vector-tile-js library created by Dane Springmeyer.\n   * To get realistic number and have a fair comparison in terms of the decoding performance,\n   * the geometries of the features have to be decoded.\n   * */\n  public static Map<String, VectorTileLayer> decodeMvtMapbox(byte[] mvtTile) throws IOException {\n    Pbf pbf = new Pbf(mvtTile);\n    VectorTile vectorTile = new VectorTile(pbf, pbf.length);\n    for (var layer : vectorTile.layers.values()) {\n      for (int i = 0; i < layer.length; i++) {\n        var feature = layer.feature(i);\n        var ignored = feature.loadGeometry();\n      }\n    }\n\n    return vectorTile.layers;\n  }\n\n  public static MapboxVectorTile decodeMvt(byte[] mvtTile) throws IOException {\n    VectorTileDecoder mvtDecoder = new VectorTileDecoder();\n    mvtDecoder.setAutoScale(false);\n\n    final var mvtFeatures = mvtDecoder.decode(mvtTile);\n    final var featuresByLayer =\n        StreamSupport.stream(mvtFeatures.spliterator(), false)\n            .collect(\n                Collectors.groupingBy(\n                    f -> f.getLayerName(), LinkedHashMap::new, Collectors.toList()));\n\n    var layers = new ArrayList<Layer>(featuresByLayer.size());\n    for (var layerGroup : featuresByLayer.entrySet()) {\n      final var layerName = layerGroup.getKey();\n      final var layerFeatures = layerGroup.getValue();\n\n      var tileExtent = 0;\n      final var features = new ArrayList<Feature>(layerFeatures.size());\n      final var builder = MVTFeature.builder();\n      for (var mvtFeature : layerFeatures) {\n        features.add(\n            builder\n                .index(features.size())\n                .id(mvtFeature.getId())\n                .geometry(mvtFeature.getGeometry())\n                .properties(mvtFeature.getAttributes())\n                .build());\n        tileExtent = Math.max(tileExtent, mvtFeature.getExtent());\n      }\n\n      layers.add(new Layer(layerName, features, tileExtent));\n    }\n\n    return new MapboxVectorTile(layers);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/tessellation/TessellatedPolygon.java",
    "content": "package org.maplibre.mlt.converter.tessellation;\n\nimport java.util.List;\n\npublic record TessellatedPolygon(List<Integer> indexBuffer, int numTriangles, int numVertices) {}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/converter/tessellation/TessellationUtils.java",
    "content": "package org.maplibre.mlt.converter.tessellation;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport jakarta.annotation.Nullable;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URI;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Stream;\nimport org.jetbrains.annotations.NotNull;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.maplibre.earcut4j.Earcut;\n\npublic class TessellationUtils {\n  private TessellationUtils() {}\n\n  public static TessellatedPolygon tessellatePolygon(\n      Polygon polygon, int indexOffset, @Nullable URI tessellateSource) {\n    final var flattenedCoordinates = flatCoordinatesWithoutClosingPoint(polygon);\n\n    final var holeIndices = new ArrayList<Integer>();\n    var numVertices = polygon.getExteriorRing().getCoordinates().length - 1;\n    for (int i = 0; i < polygon.getNumInteriorRing(); i++) {\n      holeIndices.add(numVertices);\n      numVertices += polygon.getInteriorRingN(i).getCoordinates().length - 1;\n    }\n\n    final var holeIndicesArray =\n        !holeIndices.isEmpty() ? holeIndices.stream().mapToInt(i -> i).toArray() : null;\n\n    Stream<Integer> indices;\n    if (tessellateSource != null) {\n      indices =\n          Arrays.stream(\n                  tessellatePolygonRemote(flattenedCoordinates, holeIndicesArray, tessellateSource))\n              .boxed();\n    } else {\n      indices = Earcut.earcut(flattenedCoordinates, holeIndicesArray, 2).stream();\n    }\n\n    final var indexList = indices.map(index -> index + indexOffset).toList();\n    final var numTriangles = indexList.size() / 3;\n    return new TessellatedPolygon(indexList, numTriangles, numVertices);\n  }\n\n  private static int[] tessellatePolygonRemote(\n      double[] flattenedCoordinates, int[] holeIndicesArray, @NotNull URI tessellateSource) {\n    try {\n      HttpURLConnection conn = (HttpURLConnection) tessellateSource.toURL().openConnection();\n      conn.setRequestMethod(\"POST\");\n      conn.setRequestProperty(\"Content-Type\", \"application/json\");\n      conn.setRequestProperty(\"Accept\", \"application/json\");\n      conn.setDoOutput(true);\n\n      JsonObject requestBody = new JsonObject();\n      requestBody.add(\"vertices\", new Gson().toJsonTree(flattenedCoordinates));\n      requestBody.add(\"holes\", new Gson().toJsonTree(holeIndicesArray));\n\n      try (OutputStream os = conn.getOutputStream()) {\n        byte[] input = requestBody.toString().getBytes(StandardCharsets.UTF_8);\n        os.write(input, 0, input.length);\n      }\n\n      try (BufferedReader br =\n          new BufferedReader(\n              new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {\n        StringBuilder response = new StringBuilder();\n        String responseLine;\n        while ((responseLine = br.readLine()) != null) {\n          response.append(responseLine.trim());\n        }\n\n        Gson gson = new Gson();\n        JsonObject jsonResponse = gson.fromJson(response.toString(), JsonObject.class);\n        return gson.fromJson(jsonResponse.get(\"indices\"), int[].class);\n      }\n    } catch (Exception e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  public static TessellatedPolygon tessellateMultiPolygon(\n      MultiPolygon multiPolygon, @Nullable URI tessellateSource) {\n    List<Integer> indexBuffer = new ArrayList<>();\n    var numTriangles = 0;\n\n    var numVertices = 0;\n    /* The range of the values of the indices are created per MultiPolygon,\n     *  which means the min index of every new MultiPolygon is 0.\n     *  Because of the filtering happening on the map renderer side the indices have\n     *  to be adjusted with an offset.\n     *  */\n    for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {\n      var polygon = (Polygon) multiPolygon.getGeometryN(i);\n      var tessellatedPolygon = tessellatePolygon(polygon, numVertices, tessellateSource);\n      indexBuffer.addAll(tessellatedPolygon.indexBuffer());\n\n      numTriangles += tessellatedPolygon.numTriangles();\n      // indexOffset = tessellatedPolygon.indexBuffer().stream().max(Integer::compareTo).get() + 1;\n      numVertices += tessellatedPolygon.numVertices();\n    }\n\n    return new TessellatedPolygon(indexBuffer, numTriangles, numVertices);\n  }\n\n  private static double[] flatCoordinatesWithoutClosingPoint(Polygon polygon) {\n    var shell = polygon.getExteriorRing();\n    var shellCoordinates = shell.getCoordinates();\n    var coordinates =\n        new ArrayList<>(Arrays.asList(shellCoordinates).subList(0, shellCoordinates.length - 1));\n\n    for (var i = 0; i < polygon.getNumInteriorRing(); ++i) {\n      var hole = polygon.getInteriorRingN(i);\n      var childCoordinates = hole.getCoordinates();\n      coordinates.addAll(Arrays.asList(childCoordinates).subList(0, childCoordinates.length - 1));\n    }\n\n    return coordinates.stream()\n        .flatMapToDouble(c -> Arrays.stream(new double[] {c.x, c.y}))\n        .toArray();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/Feature.java",
    "content": "package org.maplibre.mlt.data;\n\nimport jakarta.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\nimport lombok.Builder;\nimport lombok.EqualsAndHashCode;\nimport lombok.Getter;\nimport lombok.experimental.Accessors;\nimport lombok.experimental.SuperBuilder;\nimport org.jetbrains.annotations.NotNull;\nimport org.locationtech.jts.geom.Geometry;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n/** A base class for features */\n@SuperBuilder(toBuilder = true)\n@EqualsAndHashCode\npublic abstract class Feature implements FeatureInterface {\n  @Getter protected final int index;\n\n  @Accessors(fluent = true)\n  @Getter\n  @Builder.Default\n  protected final boolean hasId = false;\n\n  @Getter @Builder.Default long id = 0;\n\n  @Getter @Builder.Default @Nullable protected final Geometry geometry = null;\n\n  public Iterable<Property> getProperties() {\n    return () -> getPropertyStream().iterator();\n  }\n\n  public Stream<Property> getPropertyStream() {\n    return getPropertyStream(false);\n  }\n\n  public Optional<Property> findProperty(@NotNull Predicate<Property> predicate) {\n    return getPropertyStream().filter(predicate).findFirst();\n  }\n\n  public Optional<Property> findProperty(@NotNull String name) {\n    return findProperty(p -> p.getName().equals(name));\n  }\n\n  public Optional<Property> findProperty(\n      @NotNull String name, @NotNull MltMetadata.ScalarType type) {\n    return findProperty(name)\n        .filter(\n            p ->\n                !p.isNested()\n                    && p.getType().scalarType() != null\n                    && p.getType().scalarType().physicalType().equals(type));\n  }\n\n  /**\n   * Returns the ID as a boxed Long, or null if the feature has no ID. Use this where a nullable\n   * Long is required, e.g., intermediate lists, serialization. Prefer {@link #hasId()} and {@link\n   * #getId()} in hot paths.\n   */\n  @Nullable\n  public Long idOrNull() {\n    return hasId() ? getId() : null;\n  }\n\n  public abstract static class FeatureBuilder<C extends Feature, B extends FeatureBuilder<C, B>> {\n    public B id(long id) {\n      this.id$value = id;\n      this.id$set = true;\n      return hasId(true);\n    }\n\n    public B id(@Nullable Long id) {\n      return (id != null) ? id(id.longValue()) : hasId(false);\n    }\n  }\n\n  public abstract FeatureBuilder<?, ?> toBuilder();\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/FeatureInterface.java",
    "content": "package org.maplibre.mlt.data;\n\nimport jakarta.annotation.Nullable;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\nimport org.jetbrains.annotations.NotNull;\nimport org.locationtech.jts.geom.Geometry;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic interface FeatureInterface {\n  boolean hasId();\n\n  long getId();\n\n  @NotNull\n  Geometry getGeometry();\n\n  Iterable<Property> getProperties();\n\n  Stream<Property> getPropertyStream();\n\n  Stream<Property> getPropertyStream(boolean parallel);\n\n  Optional<Property> findProperty(@NotNull Predicate<Property> predicate);\n\n  Optional<Property> findProperty(@NotNull String name);\n\n  Optional<Property> findProperty(@NotNull String name, @NotNull MltMetadata.ScalarType type);\n\n  /**\n   * Returns the ID as a boxed Long, or null if the feature has no ID. Use this where a nullable\n   * Long is required, e.g., intermediate lists, serialization. Prefer {@link #hasId()} and {@link\n   * #getId()} in hot paths.\n   */\n  @Nullable\n  Long idOrNull();\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/IndexedProperty.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.ArrayList;\nimport java.util.SequencedCollection;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic class IndexedProperty extends Property {\n  private final SequencedCollection<Object> values;\n\n  public IndexedProperty(\n      MltMetadata.FieldType type, String name, SequencedCollection<Object> values) {\n    super(type, name, null);\n    this.values = values;\n  }\n\n  @Override\n  public Object getValue(int featureIndex) {\n    if (featureIndex < 0 || featureIndex >= values.size()) {\n      throw new IndexOutOfBoundsException(\n          \"Index \" + featureIndex + \" is out of bounds for values of size \" + values.size());\n    }\n    if (values instanceof ArrayList<?> arrayList) {\n      return arrayList.get(featureIndex);\n    }\n    return values.stream().skip(featureIndex).findFirst().orElse(null);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/Layer.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.SequencedCollection;\nimport org.jetbrains.annotations.NotNull;\n\npublic record Layer(\n    @NotNull String name, @NotNull SequencedCollection<Feature> features, int tileExtent) {}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/LayerSource.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.stream.Stream;\nimport org.jetbrains.annotations.NotNull;\n\npublic interface LayerSource {\n  default long getLayerCount() {\n    return getLayerStream().count();\n  }\n\n  @NotNull\n  default Iterable<Layer> getLayers() {\n    return () -> getLayerStream().iterator();\n  }\n\n  @NotNull\n  default Stream<Layer> getLayerStream() {\n    return getLayerStream(false);\n  }\n\n  @NotNull\n  Stream<Layer> getLayerStream(boolean parallel);\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/MLTFeature.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport lombok.Builder;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.SuperBuilder;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n/** An MLT Feature, which may have nested properties that are not supported in MVT. */\n@SuperBuilder(toBuilder = true)\n@EqualsAndHashCode(callSuper = true)\npublic class MLTFeature extends Feature {\n  @NotNull @Builder.Default private final Map<String, Property> properties = Map.of();\n\n  @Override\n  public Optional<Property> findProperty(String name) {\n    return Optional.ofNullable(properties.get(name));\n  }\n\n  @Override\n  public Stream<Property> getPropertyStream(boolean parallel) {\n    final var entries = properties.entrySet();\n    return (parallel ? entries.parallelStream() : entries.stream()).map(Map.Entry::getValue);\n  }\n\n  static Property adapt(String name, Object value) {\n    return new Property(getType(value), name, value);\n  }\n\n  static Property adapt(Map.Entry<String, Object> entry) {\n    return adapt(entry.getKey(), entry.getValue());\n  }\n\n  static MltMetadata.FieldType getType(Object value) {\n    if (value instanceof Map<?, ?>) {\n      return MltMetadata.complexFieldType(MltMetadata.ComplexType.MAP, true);\n    }\n    final var isNullable = true;\n    return MltMetadata.scalarFieldType(MVTFeature.getType(value), isNullable);\n  }\n\n  /**\n   * Builder class for #MLTFeature\n   *\n   * @param <B> The final builder type, for covariance in property setters.\n   * @param <C> The final feature type, for covariance in the build() method.\n   */\n  public abstract static class MLTFeatureBuilder<\n          C extends MLTFeature, B extends MLTFeatureBuilder<C, B>>\n      extends Feature.FeatureBuilder<C, B> {\n    /**\n     * Set properties from a Map of raw values, which will be adapted to Property objects.\n     *\n     * @param rawProperties A Map of String keys to Object values\n     * @return The builder instance, for chaining\n     */\n    public B rawProperties(Map<String, Object> rawProperties) {\n      return properties(\n          rawProperties.entrySet().stream()\n              .collect(Collectors.toMap(Map.Entry::getKey, MLTFeature::adapt)));\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/MVTFeature.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.math.BigInteger;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.stream.Stream;\nimport lombok.Builder;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.SuperBuilder;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.data.unsigned.U32;\nimport org.maplibre.mlt.data.unsigned.U64;\nimport org.maplibre.mlt.data.unsigned.U8;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n@SuperBuilder(toBuilder = true)\n@EqualsAndHashCode(callSuper = true)\npublic class MVTFeature extends Feature {\n  @NotNull @Builder.Default private final Map<String, Object> properties = Map.of();\n\n  public Map<String, Object> getRawProperties() {\n    return properties;\n  }\n\n  @Override\n  public Optional<Property> findProperty(String name) {\n    return Optional.ofNullable(properties.get(name)).map(value -> adapt(name, value));\n  }\n\n  @Override\n  public Stream<Property> getPropertyStream(boolean parallel) {\n    final var entries = properties.entrySet();\n    return (parallel ? entries.parallelStream() : entries.stream()).map(MVTFeature::adapt);\n  }\n\n  static Property adapt(String name, Object value) {\n    final var type = getType(value);\n    final var isNullable = true;\n    return new Property(MltMetadata.scalarFieldType(type, isNullable), name, value);\n  }\n\n  static Property adapt(Map.Entry<String, Object> entry) {\n    return adapt(entry.getKey(), entry.getValue());\n  }\n\n  static MltMetadata.ScalarType getType(Object value) {\n    return switch (value) {\n      case null -> MltMetadata.ScalarType.UNRECOGNIZED;\n      case String ignored -> MltMetadata.ScalarType.STRING;\n      case Boolean ignored -> MltMetadata.ScalarType.BOOLEAN;\n      case Double ignored -> MltMetadata.ScalarType.DOUBLE;\n      case Float ignored -> MltMetadata.ScalarType.FLOAT;\n      case U8 ignored -> MltMetadata.ScalarType.UINT_8;\n      case Integer ignored -> MltMetadata.ScalarType.INT_32;\n      case U32 ignored -> MltMetadata.ScalarType.UINT_32;\n      case Long l ->\n          (l.intValue() == l) ? MltMetadata.ScalarType.INT_32 : MltMetadata.ScalarType.INT_64;\n      case U64 ignored -> MltMetadata.ScalarType.UINT_64;\n      case BigInteger i ->\n          (i.intValue() == i.longValue())\n              ? MltMetadata.ScalarType.INT_32\n              : MltMetadata.ScalarType.INT_64;\n      default ->\n          throw new IllegalArgumentException(\n              \"Unsupported property value type: \" + value.getClass());\n    };\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/MapLibreTile.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.Collection;\nimport java.util.stream.Stream;\nimport org.jetbrains.annotations.NotNull;\n\npublic class MapLibreTile implements LayerSource {\n  @NotNull private final Collection<Layer> layers;\n\n  public MapLibreTile(@NotNull Collection<Layer> layers) {\n    this.layers = layers;\n  }\n\n  @Override\n  public @NotNull Stream<Layer> getLayerStream(boolean parallel) {\n    return parallel ? layers.parallelStream() : layers.stream();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/MapboxVectorTile.java",
    "content": "package org.maplibre.mlt.data;\n\nimport jakarta.annotation.Nullable;\nimport java.util.SequencedCollection;\nimport java.util.stream.Stream;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.jetbrains.annotations.NotNull;\n\npublic class MapboxVectorTile implements LayerSource {\n  private @NotNull SequencedCollection<Layer> layers;\n  private @Nullable Triple<Integer, Integer, Integer> tileId;\n\n  public MapboxVectorTile(@NotNull SequencedCollection<Layer> layers) {\n    this.layers = layers;\n  }\n\n  public MapboxVectorTile(\n      @NotNull SequencedCollection<Layer> layers,\n      @Nullable Triple<Integer, Integer, Integer> tileId) {\n    this(layers);\n    this.tileId = tileId;\n  }\n\n  public void setTileId(@Nullable Triple<Integer, Integer, Integer> tileId) {\n    this.tileId = tileId;\n  }\n\n  public @Nullable Triple<Integer, Integer, Integer> tileId() {\n    return tileId;\n  }\n\n  @Override\n  public long getLayerCount() {\n    return layers.size();\n  }\n\n  @Override\n  public @NotNull Stream<Layer> getLayerStream(boolean parallel) {\n    return parallel ? layers.parallelStream() : layers.stream();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/Property.java",
    "content": "package org.maplibre.mlt.data;\n\nimport java.util.Objects;\nimport lombok.Getter;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n/** Represents a property of a feature */\npublic class Property {\n  @Getter @NotNull private final MltMetadata.FieldType type;\n  @Getter private final String name;\n  private final Object value;\n\n  /**\n   * Create directly from a complex type\n   *\n   * @param type The type of the property\n   * @param name The name of the property\n   * @param value The value of the property, which should be compatible with the type. No validation\n   *     is performed.\n   */\n  public Property(@NotNull MltMetadata.FieldType type, String name, Object value) {\n    this.type = Objects.requireNonNull(type);\n    this.name = name;\n    this.value = value;\n\n    if (!isNested(type) && value != null) {\n      if (type.scalarType() == null\n          || type.scalarType().physicalType().ordinal() < MltMetadata.ScalarType.BOOLEAN.ordinal()\n          || type.scalarType().physicalType().ordinal() > MltMetadata.ScalarType.STRING.ordinal()) {\n        throw new IllegalArgumentException(\n            \"FieldType must have either a valid scalarType or a complexType of MAP\");\n      }\n    }\n  }\n\n  /**\n   * @param featureIndex The index of the feature for which to get the value\n   * @return The value of the property.\n   */\n  public Object getValue(int featureIndex) {\n    return value;\n  }\n\n  private static boolean isNested(MltMetadata.FieldType type) {\n    return (type.complexType() != null\n        && type.complexType().physicalType() == MltMetadata.ComplexType.MAP);\n  }\n\n  /**\n   * @return True if the property is nested\n   */\n  public boolean isNested() {\n    return isNested(type);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/unsigned/U32.java",
    "content": "package org.maplibre.mlt.data.unsigned;\n\npublic record U32(int value) implements Unsigned {\n\n  public static U32 of(long value) {\n    if (value < 0 || value > 0xFFFFFFFFL) {\n      throw new IllegalArgumentException(\"Out of range for u32\");\n    }\n    return new U32((int) value);\n  }\n\n  @Override\n  public Byte byteValue() {\n    final var v = value;\n    if ((byte) v == v) {\n      return (byte) v;\n    }\n    return null;\n  }\n\n  @Override\n  public Integer intValue() {\n    return value;\n  }\n\n  @Override\n  public Long longValue() {\n    return Integer.toUnsignedLong(value);\n  }\n\n  @Override\n  public String toString() {\n    return \"u32(\" + Integer.toUnsignedLong(value) + \")\";\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/unsigned/U64.java",
    "content": "package org.maplibre.mlt.data.unsigned;\n\nimport java.math.BigInteger;\n\npublic record U64(long value) implements Unsigned {\n\n  public static U64 of(BigInteger value) {\n    if (value.signum() < 0 || value.bitLength() > 64) {\n      throw new IllegalArgumentException(\"Out of range for u64\");\n    }\n    return new U64(value.longValue());\n  }\n\n  @Override\n  public Byte byteValue() {\n    final var v = value;\n    if ((byte) v == v) {\n      return (byte) v;\n    }\n    return null;\n  }\n\n  @Override\n  public Integer intValue() {\n    final var v = value;\n    if ((int) v == v) {\n      return (int) v;\n    }\n    return null;\n  }\n\n  @Override\n  public Long longValue() {\n    return value;\n  }\n\n  @Override\n  public String toString() {\n    return \"u64(\" + Long.toUnsignedString(value) + \")\";\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/unsigned/U8.java",
    "content": "package org.maplibre.mlt.data.unsigned;\n\npublic record U8(byte value) implements Unsigned {\n\n  public static U8 of(int value) {\n    if (value < 0 || value > 255) {\n      throw new IllegalArgumentException(\"Out of range for u8\");\n    }\n    return new U8((byte) value);\n  }\n\n  @Override\n  public Byte byteValue() {\n    return value;\n  }\n\n  @Override\n  public Integer intValue() {\n    return Byte.toUnsignedInt(value);\n  }\n\n  @Override\n  public Long longValue() {\n    return Byte.toUnsignedLong(value);\n  }\n\n  @Override\n  public String toString() {\n    return \"u8(\" + Byte.toUnsignedInt(value) + \")\";\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/data/unsigned/Unsigned.java",
    "content": "package org.maplibre.mlt.data.unsigned;\n\n/**\n * Represents an unsigned integer of a fixed bit width (8, 32, or 64 bits).\n *\n * <p>This interface abstracts over different unsigned integer types in Java, which do not natively\n * support unsigned primitives, providing a common API for working with unsigned values regardless\n * of their underlying representation.\n */\npublic sealed interface Unsigned permits U8, U32, U64 {\n  Byte byteValue();\n\n  Integer intValue();\n\n  Long longValue();\n\n  @Override\n  String toString();\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/ByteRleDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.nio.ByteBuffer;\n\n/**\n * Decodes byte run-length encoded data.\n *\n * <p>The encoding format uses a control byte followed by data:\n *\n * <ul>\n *   <li>Control byte 0x00-0x7F: Run of (control + 3) copies of the next byte\n *   <li>Control byte 0x80-0xFF: (256 - control) literal bytes follow\n * </ul>\n */\npublic class ByteRleDecoder {\n  private static final int MIN_REPEAT_SIZE = 3;\n\n  private final ByteBuffer buffer;\n  private final byte[] literals = new byte[128];\n  private int numLiterals = 0;\n  private int used = 0;\n  private boolean repeat = false;\n\n  public ByteRleDecoder(byte[] data, int offset, int length) {\n    this.buffer = ByteBuffer.wrap(data, offset, length);\n  }\n\n  private void readValues() {\n    int control = buffer.get() & 0xFF;\n\n    if (control < 0x80) {\n      // Run: control + MIN_REPEAT_SIZE copies of the next byte\n      repeat = true;\n      numLiterals = control + MIN_REPEAT_SIZE;\n      literals[0] = buffer.get();\n    } else {\n      // Literals: 256 - control literal bytes\n      repeat = false;\n      numLiterals = 256 - control;\n      buffer.get(literals, 0, numLiterals);\n    }\n    used = 0;\n  }\n\n  public byte next() {\n    if (used == numLiterals) {\n      readValues();\n    }\n    if (repeat) {\n      used++;\n      return literals[0];\n    } else {\n      return literals[used++];\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/DecodingUtils.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.IntBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport me.lemire.integercompression.Composition;\nimport me.lemire.integercompression.FastPFOR;\nimport me.lemire.integercompression.IntWrapper;\nimport me.lemire.integercompression.IntegerCODEC;\nimport me.lemire.integercompression.VariableByte;\nimport org.apache.commons.lang3.tuple.Pair;\n\npublic class DecodingUtils {\n\n  private DecodingUtils() {}\n\n  // TODO: quick and dirty -> optimize for performance\n  public static int[] decodeVarints(byte[] src, IntWrapper pos, int numValues) throws IOException {\n    var values = new int[numValues];\n    var dstOffset = 0;\n    for (var i = 0; i < numValues; i++) {\n      var offset = decodeVarint(src, pos.get(), values, dstOffset);\n      dstOffset++;\n      pos.set(offset);\n    }\n    return values;\n  }\n\n  public static long[] decodeLongVarints(byte[] src, IntWrapper pos, int numValues) {\n    var values = new long[numValues];\n    for (var i = 0; i < numValues; i++) {\n      var value = decodeLongVarint(src, pos);\n      values[i] = value;\n    }\n    return values;\n  }\n\n  public static long decodeLongVarint(byte[] bytes, IntWrapper pos) {\n    // TODO: write faster decoding method for varint\n    long value = 0;\n    int shift = 0;\n    int index = pos.get();\n    while (index < bytes.length) {\n      byte b = bytes[index++];\n      value |= (long) (b & 0x7F) << shift;\n      if ((b & 0x80) == 0) {\n        break;\n      }\n      shift += 7;\n      if (shift > 63) {\n        throw new IllegalArgumentException(\"Varint too long\");\n      }\n    }\n\n    pos.set(index);\n    return value;\n  }\n\n  // Source:\n  // https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/util/VarInt.java\n  /**\n   * Reads a varint from src, places its values into the first element of dst and returns the offset\n   * in to src of the first byte after the varint.\n   *\n   * @param src source buffer to retrieve from\n   * @param srcOffset offset within src\n   * @param dst the resulting int values\n   * @param dstOffset offset with dst\n   * @return the updated offset after reading the varint\n   */\n  private static int decodeVarint(byte[] src, int srcOffset, int[] dst, int dstOffset)\n      throws IOException {\n    try (var stream = new ByteArrayInputStream(src, srcOffset, src.length - srcOffset)) {\n      final var result = decodeVarintWithLength(stream);\n      dst[dstOffset] = result.getLeft();\n      return srcOffset + result.getRight();\n    }\n  }\n\n  public static Pair<Integer, Integer> decodeVarintWithLength(InputStream stream)\n      throws IOException {\n    var b = (byte) stream.read();\n    var bytesRead = 1;\n    int value = b & 0x7f;\n    if ((b & 0x80) != 0) {\n      b = (byte) stream.read();\n      bytesRead++;\n      value |= (b & 0x7f) << 7;\n      if ((b & 0x80) != 0) {\n        b = (byte) stream.read();\n        bytesRead++;\n        value |= (b & 0x7f) << 14;\n        if ((b & 0x80) != 0) {\n          b = (byte) stream.read();\n          bytesRead++;\n          value |= (b & 0x7f) << 21;\n          if ((b & 0x80) != 0) {\n            b = (byte) stream.read();\n            bytesRead++;\n            value |= (b & 0x7f) << 28;\n            if ((b & 0x80) != 0 || 15 < b) {\n              throw new IOException(\"Varint overflow\");\n            }\n          }\n        }\n      }\n    }\n    return Pair.of(value, bytesRead);\n  }\n\n  public static int decodeVarint(InputStream stream) throws IOException {\n    return decodeVarintWithLength(stream).getLeft();\n  }\n\n  public static String decodeString(InputStream stream) throws IOException {\n    var length = decodeVarint(stream);\n    return new String(stream.readNBytes(length), StandardCharsets.UTF_8);\n  }\n\n  public static int decodeZigZag(int encoded) {\n    return (encoded >>> 1) ^ (-(encoded & 1));\n  }\n\n  public static long decodeZigZag(long encoded) {\n    return (encoded >>> 1) ^ (-(encoded & 1));\n  }\n\n  public static int[] decodeFastPfor(\n      byte[] encodedValues, int numValues, int byteLength, IntWrapper pos) {\n    var encodedValuesSlice = Arrays.copyOfRange(encodedValues, pos.get(), pos.get() + byteLength);\n    // TODO: get rid of that conversion\n    IntBuffer intBuf =\n        ByteBuffer.wrap(encodedValuesSlice)\n            // TODO: change to little endian\n            .order(ByteOrder.BIG_ENDIAN)\n            .asIntBuffer();\n    int[] intValues = new int[(int) Math.ceil(byteLength / 4d)];\n    for (var i = 0; i < intValues.length; i++) {\n      intValues[i] = intBuf.get(i);\n    }\n\n    int[] decodedValues = new int[numValues];\n    var inputOffset = new IntWrapper(0);\n    var outputOffset = new IntWrapper(0);\n    IntegerCODEC ic = new Composition(new FastPFOR(), new VariableByte());\n    ic.uncompress(intValues, inputOffset, intValues.length, decodedValues, outputOffset);\n\n    pos.add(byteLength);\n    return decodedValues;\n  }\n\n  public static int[] decodeFastPforDeltaCoordinates(\n      byte[] encodedValues, int numValues, int byteLength, IntWrapper pos) {\n    var encodedValuesSlice = Arrays.copyOfRange(encodedValues, pos.get(), pos.get() + byteLength);\n    // TODO: get rid of that conversion\n    IntBuffer intBuf =\n        ByteBuffer.wrap(encodedValuesSlice)\n            // TODO: change to little endian\n            .order(ByteOrder.BIG_ENDIAN)\n            .asIntBuffer();\n    int[] intValues = new int[(int) Math.ceil(byteLength / 4d)];\n    for (var i = 0; i < intValues.length; i++) {\n      intValues[i] = intBuf.get(i);\n    }\n\n    int[] decompressedValues = new int[numValues];\n    var inputOffset = new IntWrapper(0);\n    var outputOffset = new IntWrapper(0);\n    IntegerCODEC ic = new Composition(new FastPFOR(), new VariableByte());\n    ic.uncompress(intValues, inputOffset, intValues.length, decompressedValues, outputOffset);\n\n    var decodedValues = new int[numValues];\n    for (var i = 0; i < numValues; i++) {\n      var zigZagValue = decompressedValues[i];\n      decodedValues[i] = (zigZagValue >>> 1) ^ (-(zigZagValue & 1));\n    }\n\n    pos.set(pos.get() + byteLength);\n\n    var values = new int[numValues];\n    var previousValueX = 0;\n    var previousValueY = 0;\n    for (var i = 0; i < numValues; i += 2) {\n      var deltaX = decodedValues[i];\n      var deltaY = decodedValues[i + 1];\n      var x = previousValueX + deltaX;\n      var y = previousValueY + deltaY;\n      values[i] = x;\n      values[i + 1] = y;\n\n      previousValueX = x;\n      previousValueY = y;\n    }\n\n    return values;\n  }\n\n  public static byte[] decodeByteRle(byte[] buffer, int numBytes, int byteSize, IntWrapper pos) {\n    var reader = new ByteRleDecoder(buffer, pos.get(), byteSize);\n\n    var values = new byte[numBytes];\n    for (var i = 0; i < numBytes; i++) {\n      values[i] = reader.next();\n    }\n\n    pos.add(byteSize);\n    return values;\n  }\n\n  public static BitSet decodeBooleanRle(\n      byte[] buffer, int numBooleans, int byteSize, IntWrapper pos) {\n    var numBytes = (int) Math.ceil(numBooleans / 8d);\n    var byteStream = decodeByteRle(buffer, numBytes, byteSize, pos);\n    // TODO: get rid of that conversion\n    return BitSet.valueOf(byteStream);\n  }\n\n  public static int[] decodeUnsignedRLE(int[] data, int numRuns, int numTotalValues) {\n    var values = new int[numTotalValues];\n    var offset = 0;\n    for (var i = 0; i < numRuns; i++) {\n      var runLength = data[i];\n      var value = data[i + numRuns];\n      for (var j = offset; j < offset + runLength; j++) {\n        values[j] = value;\n      }\n\n      offset += runLength;\n    }\n    return values;\n  }\n\n  public static long[] decodeUnsignedRLE(long[] data, int numRuns, int numTotalValues) {\n    var values = new long[numTotalValues];\n    var offset = 0L;\n    for (var i = 0; i < numRuns; i++) {\n      var runLength = data[i];\n      var value = data[i + numRuns];\n      for (var j = offset; j < offset + runLength; j++) {\n        values[(int) j] = value;\n      }\n\n      offset += runLength;\n    }\n    return values;\n  }\n\n  public static float[] decodeFloatsLE(byte[] encodedValues, IntWrapper pos, int numValues) {\n    var fb =\n        ByteBuffer.wrap(encodedValues, pos.get(), numValues * Float.BYTES)\n            .order(ByteOrder.LITTLE_ENDIAN)\n            .asFloatBuffer();\n    pos.set(pos.get() + numValues * Float.BYTES);\n    var decodedValues = new float[fb.limit()];\n    fb.get(decodedValues);\n    return decodedValues;\n  }\n\n  public static double[] decodeDoublesLE(byte[] encodedValues, IntWrapper pos, int numValues) {\n    var fb =\n        ByteBuffer.wrap(encodedValues, pos.get(), numValues * Double.BYTES)\n            .order(ByteOrder.LITTLE_ENDIAN)\n            .asDoubleBuffer();\n    pos.set(pos.get() + numValues * Double.BYTES);\n    var decodedValues = new double[fb.limit()];\n    fb.get(decodedValues);\n    return decodedValues;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/DoubleDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class DoubleDecoder {\n  private DoubleDecoder() {}\n\n  public static List<Double> decodeDoubleStream(\n      byte[] data, IntWrapper offset, StreamMetadata streamMetadata) {\n    if ((long) streamMetadata.numValues() * Double.BYTES == streamMetadata.byteLength()) {\n      final var values = DecodingUtils.decodeDoublesLE(data, offset, streamMetadata.numValues());\n      return Arrays.stream(values).boxed().collect(Collectors.toUnmodifiableList());\n    } else {\n      // Compatibility with tilesets encoded before double support was added\n      final var values = DecodingUtils.decodeFloatsLE(data, offset, streamMetadata.numValues());\n      return IntStream.range(0, values.length)\n          .mapToDouble(i -> values[i])\n          .boxed()\n          .collect(Collectors.toUnmodifiableList());\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/FloatDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class FloatDecoder {\n  private FloatDecoder() {}\n\n  public static List<Float> decodeFloatStream(\n      byte[] data, IntWrapper offset, StreamMetadata streamMetadata) {\n    var values = DecodingUtils.decodeFloatsLE(data, offset, streamMetadata.numValues());\n    var valuesList = new ArrayList<Float>(values.length);\n    for (var value : values) {\n      valuesList.add(value);\n    }\n    return valuesList;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/GeometryDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport me.lemire.integercompression.IntWrapper;\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.maplibre.mlt.converter.MortonSettings;\nimport org.maplibre.mlt.converter.geometry.GeometryType;\nimport org.maplibre.mlt.converter.geometry.ZOrderCurve;\nimport org.maplibre.mlt.metadata.stream.DictionaryType;\nimport org.maplibre.mlt.metadata.stream.MortonEncodedStreamMetadata;\nimport org.maplibre.mlt.metadata.stream.OffsetType;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.StreamMetadataDecoder;\n\npublic class GeometryDecoder {\n\n  public record GeometryColumn(\n      List<Integer> geometryTypes,\n      List<Integer> numGeometries,\n      List<Integer> numParts,\n      List<Integer> numRings,\n      List<Integer> vertexOffsets,\n      List<Integer> vertexList,\n      List<Integer> triangles,\n      List<Integer> indexOffsets) {}\n\n  private GeometryDecoder() {}\n\n  public static GeometryColumn decodeGeometryColumn(byte[] tile, int numStreams, IntWrapper offset)\n      throws IOException {\n    var geometryTypeMetadata = StreamMetadataDecoder.decode(tile, offset);\n    var geometryTypes = IntegerDecoder.decodeIntStream(tile, offset, geometryTypeMetadata, false);\n\n    List<Integer> numGeometries = null;\n    List<Integer> numParts = null;\n    List<Integer> numRings = null;\n    List<Integer> vertexOffsets = null;\n    List<Integer> indexOffsets = null;\n    List<Integer> vertexList = null;\n    List<Integer> triangles = null;\n    for (var i = 0; i < numStreams - 1; i++) {\n      var geometryStreamMetadata = StreamMetadataDecoder.decode(tile, offset);\n      switch (geometryStreamMetadata.physicalStreamType()) {\n        case LENGTH:\n          switch (geometryStreamMetadata.logicalStreamType().lengthType()) {\n            case GEOMETRIES:\n              numGeometries =\n                  IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, false);\n              break;\n            case PARTS:\n              numParts =\n                  IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, false);\n              break;\n            case RINGS:\n              numRings =\n                  IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, false);\n              break;\n            case TRIANGLES:\n              triangles =\n                  IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, false);\n              break;\n          }\n          break;\n        case OFFSET:\n          {\n            final var values =\n                IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, false);\n            final var type = geometryStreamMetadata.logicalStreamType().offsetType();\n            if (type == OffsetType.VERTEX) {\n              vertexOffsets = values;\n            } else if (type == OffsetType.INDEX) {\n              indexOffsets = values;\n            } else {\n              throw new RuntimeException(\"Unexpected offset stream \" + type);\n            }\n          }\n          break;\n        case DATA:\n          if (DictionaryType.VERTEX.equals(\n              geometryStreamMetadata.logicalStreamType().dictionaryType())) {\n            if (geometryStreamMetadata.physicalLevelTechnique()\n                == PhysicalLevelTechnique.FAST_PFOR) {\n              var vertexBuffer =\n                  DecodingUtils.decodeFastPforDeltaCoordinates(\n                      tile,\n                      geometryStreamMetadata.numValues(),\n                      geometryStreamMetadata.byteLength(),\n                      offset);\n              vertexList = Arrays.stream(vertexBuffer).boxed().collect(Collectors.toList());\n            } else {\n              vertexList =\n                  IntegerDecoder.decodeIntStream(tile, offset, geometryStreamMetadata, true);\n            }\n          } else {\n            vertexList =\n                IntegerDecoder.decodeMortonStream(\n                    tile, offset, (MortonEncodedStreamMetadata) geometryStreamMetadata);\n          }\n          break;\n      }\n    }\n\n    return new GeometryColumn(\n        geometryTypes,\n        numGeometries,\n        numParts,\n        numRings,\n        vertexOffsets,\n        vertexList,\n        triangles,\n        indexOffsets);\n  }\n\n  public static Geometry[] decodeGeometry(GeometryColumn geometryColumn) {\n    var geometries = new Geometry[geometryColumn.geometryTypes.size()];\n    var partOffsetCounter = 0;\n    var ringOffsetsCounter = 0;\n    var geometryOffsetsCounter = 0;\n    var geometryCounter = 0;\n    var geometryFactory = new GeometryFactory();\n    var vertexBufferOffset = 0;\n    var vertexOffsetsOffset = 0;\n\n    var geometryTypes = geometryColumn.geometryTypes();\n    var geometryOffsets = geometryColumn.numGeometries();\n    var partOffsets = geometryColumn.numParts();\n    var ringOffsets = geometryColumn.numRings();\n    var vertexOffsets =\n        geometryColumn.vertexOffsets() != null\n            ? geometryColumn.vertexOffsets().stream().mapToInt(i -> i).toArray()\n            : null;\n\n    var vertexBuffer = geometryColumn.vertexList.stream().mapToInt(i -> i).toArray();\n\n    final var containsPolygon = containsPolygon(geometryTypes);\n\n    // TODO: refactor redundant code\n    for (var geometryType : geometryTypes) {\n      if (geometryType.equals(GeometryType.POINT.ordinal())) {\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          var x = vertexBuffer[vertexBufferOffset++];\n          var y = vertexBuffer[vertexBufferOffset++];\n          var coordinate = new Coordinate(x, y);\n          geometries[geometryCounter++] = geometryFactory.createPoint(coordinate);\n        } else {\n          var offset = vertexOffsets[vertexOffsetsOffset++] * 2;\n          var x = vertexBuffer[offset];\n          var y = vertexBuffer[offset + 1];\n          var coordinate = new Coordinate(x, y);\n          geometries[geometryCounter++] = geometryFactory.createPoint(coordinate);\n        }\n      } else if (geometryType.equals(GeometryType.MULTIPOINT.ordinal())) {\n        var numPoints = geometryOffsets.get(geometryOffsetsCounter++);\n        var points = new Point[numPoints];\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          for (var i = 0; i < numPoints; i++) {\n            var x = vertexBuffer[vertexBufferOffset++];\n            var y = vertexBuffer[vertexBufferOffset++];\n            var coordinate = new Coordinate(x, y);\n            points[i] = geometryFactory.createPoint(coordinate);\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiPoint(points);\n        } else {\n          for (var i = 0; i < numPoints; i++) {\n            var offset = vertexOffsets[vertexOffsetsOffset++] * 2;\n            var x = vertexBuffer[offset];\n            var y = vertexBuffer[offset + 1];\n            var coordinate = new Coordinate(x, y);\n            points[i] = geometryFactory.createPoint(coordinate);\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiPoint(points);\n        }\n      } else if (geometryType.equals(GeometryType.LINESTRING.ordinal())) {\n        var numVertices =\n            containsPolygon\n                ? ringOffsets.get(ringOffsetsCounter++)\n                : partOffsets.get(partOffsetCounter++);\n\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          var vertices = getLineString(vertexBuffer, vertexBufferOffset, numVertices, false);\n          vertexBufferOffset += numVertices * 2;\n          geometries[geometryCounter++] = geometryFactory.createLineString(vertices);\n        } else {\n          var vertices =\n              decodeDictionaryEncodedLineString(\n                  vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, false);\n          vertexOffsetsOffset += numVertices;\n\n          geometries[geometryCounter++] = geometryFactory.createLineString(vertices);\n        }\n      } else if (geometryType.equals(GeometryType.POLYGON.ordinal())) {\n        var numRings = partOffsets.get(partOffsetCounter++);\n        var rings = new LinearRing[numRings - 1];\n        var numVertices = ringOffsets.get(ringOffsetsCounter++);\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          LinearRing shell =\n              getLinearRing(vertexBuffer, vertexBufferOffset, numVertices, geometryFactory);\n          vertexBufferOffset += numVertices * 2;\n          for (var i = 0; i < rings.length; i++) {\n            numVertices = ringOffsets.get(ringOffsetsCounter++);\n            rings[i] =\n                getLinearRing(vertexBuffer, vertexBufferOffset, numVertices, geometryFactory);\n            vertexBufferOffset += numVertices * 2;\n          }\n          geometries[geometryCounter++] = geometryFactory.createPolygon(shell, rings);\n        } else {\n          LinearRing shell =\n              decodeDictionaryEncodedLinearRing(\n                  vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, geometryFactory);\n          vertexOffsetsOffset += numVertices;\n          for (var i = 0; i < rings.length; i++) {\n            numVertices = ringOffsets.get(ringOffsetsCounter++);\n            rings[i] =\n                decodeDictionaryEncodedLinearRing(\n                    vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, geometryFactory);\n            vertexOffsetsOffset += numVertices;\n          }\n          geometries[geometryCounter++] = geometryFactory.createPolygon(shell, rings);\n        }\n      } else if (geometryType.equals(GeometryType.MULTILINESTRING.ordinal())) {\n        var numLineStrings = geometryOffsets.get(geometryOffsetsCounter++);\n        var lineStrings = new LineString[numLineStrings];\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          for (var i = 0; i < numLineStrings; i++) {\n            var numVertices =\n                containsPolygon\n                    ? ringOffsets.get(ringOffsetsCounter++)\n                    : partOffsets.get(partOffsetCounter++);\n\n            var vertices = getLineString(vertexBuffer, vertexBufferOffset, numVertices, false);\n            lineStrings[i] = geometryFactory.createLineString(vertices);\n            vertexBufferOffset += numVertices * 2;\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiLineString(lineStrings);\n        } else {\n          for (var i = 0; i < numLineStrings; i++) {\n            var numVertices =\n                containsPolygon\n                    ? ringOffsets.get(ringOffsetsCounter++)\n                    : partOffsets.get(partOffsetCounter++);\n\n            var vertices =\n                decodeDictionaryEncodedLineString(\n                    vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, false);\n            lineStrings[i] = geometryFactory.createLineString(vertices);\n            vertexOffsetsOffset += numVertices;\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiLineString(lineStrings);\n        }\n      } else if (geometryType.equals(GeometryType.MULTIPOLYGON.ordinal())) {\n        var numPolygons = geometryOffsets.get(geometryOffsetsCounter++);\n        var polygons = new Polygon[numPolygons];\n        var numVertices = 0;\n        if (vertexOffsets == null || vertexOffsets.length == 0) {\n          for (var i = 0; i < numPolygons; i++) {\n            var numRings = partOffsets.get(partOffsetCounter++);\n            var rings = new LinearRing[numRings - 1];\n            numVertices = ringOffsets.get(ringOffsetsCounter++);\n            LinearRing shell =\n                getLinearRing(vertexBuffer, vertexBufferOffset, numVertices, geometryFactory);\n            vertexBufferOffset += numVertices * 2;\n            for (var j = 0; j < rings.length; j++) {\n              var numRingVertices = ringOffsets.get(ringOffsetsCounter++);\n              rings[j] =\n                  getLinearRing(vertexBuffer, vertexBufferOffset, numRingVertices, geometryFactory);\n              vertexBufferOffset += numRingVertices * 2;\n            }\n\n            polygons[i] = geometryFactory.createPolygon(shell, rings);\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiPolygon(polygons);\n        } else {\n          for (var i = 0; i < numPolygons; i++) {\n            var numRings = partOffsets.get(partOffsetCounter++);\n            var rings = new LinearRing[numRings - 1];\n            numVertices = ringOffsets.get(ringOffsetsCounter++);\n            LinearRing shell =\n                decodeDictionaryEncodedLinearRing(\n                    vertexBuffer, vertexOffsets, vertexOffsetsOffset, numVertices, geometryFactory);\n            vertexOffsetsOffset += numVertices;\n            for (var j = 0; j < rings.length; j++) {\n              numVertices = ringOffsets.get(ringOffsetsCounter++);\n              rings[j] =\n                  decodeDictionaryEncodedLinearRing(\n                      vertexBuffer,\n                      vertexOffsets,\n                      vertexOffsetsOffset,\n                      numVertices,\n                      geometryFactory);\n              vertexOffsetsOffset += numVertices;\n            }\n            polygons[i] = geometryFactory.createPolygon(shell, rings);\n          }\n          geometries[geometryCounter++] = geometryFactory.createMultiPolygon(polygons);\n        }\n      } else {\n        throw new IllegalArgumentException(\n            \"The specified geometry type is currently not supported: \" + geometryType);\n      }\n    }\n\n    return geometries;\n  }\n\n  private static LinearRing getLinearRing(\n      int[] vertexBuffer, int startIndex, int numVertices, GeometryFactory geometryFactory) {\n    var linearRing = getLineString(vertexBuffer, startIndex, numVertices, true);\n    return geometryFactory.createLinearRing(linearRing);\n  }\n\n  private static LinearRing decodeDictionaryEncodedLinearRing(\n      int[] vertexBuffer,\n      int[] vertexOffsets,\n      int vertexOffset,\n      int numVertices,\n      GeometryFactory geometryFactory) {\n    var linearRing =\n        decodeDictionaryEncodedLineString(\n            vertexBuffer, vertexOffsets, vertexOffset, numVertices, true);\n    return geometryFactory.createLinearRing(linearRing);\n  }\n\n  private static Coordinate[] getLineString(\n      int[] vertexBuffer, int startIndex, int numVertices, boolean closeLineString) {\n    var vertices = new Coordinate[closeLineString ? numVertices + 1 : numVertices];\n    for (var i = 0; i < numVertices * 2; i += 2) {\n      var x = vertexBuffer[startIndex + i];\n      var y = vertexBuffer[startIndex + i + 1];\n      vertices[i / 2] = new Coordinate(x, y);\n    }\n\n    if (closeLineString) {\n      vertices[vertices.length - 1] = vertices[0];\n    }\n    return vertices;\n  }\n\n  private static Coordinate[] decodeDictionaryEncodedLineString(\n      int[] vertexBuffer,\n      int[] vertexOffsets,\n      int vertexOffset,\n      int numVertices,\n      boolean closeLineString) {\n    var vertices = new Coordinate[closeLineString ? numVertices + 1 : numVertices];\n    for (var i = 0; i < numVertices * 2; i += 2) {\n      var offset = vertexOffsets[vertexOffset + i / 2] * 2;\n      var x = vertexBuffer[offset];\n      var y = vertexBuffer[offset + 1];\n      vertices[i / 2] = new Coordinate(x, y);\n    }\n\n    if (closeLineString) {\n      vertices[vertices.length - 1] = vertices[0];\n    }\n    return vertices;\n  }\n\n  /*\n   * The decoding of the Morton encoded vertices can happen completely in parallel on the GPU in the Vertex or Compute Shader.\n   * Therefore, the decoding of the Morton encoded vertices is not part of the decoding benchmark from the storage into the\n   * in-memory representation.\n   * */\n  private static Coordinate[] decodeMortonDictionaryEncodedLineString(\n      int[] vertexBuffer,\n      int[] vertexOffsets,\n      int vertexOffset,\n      int numVertices,\n      boolean closeLineString,\n      MortonSettings mortonSettings) {\n    var vertices = new Coordinate[closeLineString ? numVertices + 1 : numVertices];\n    for (var i = 0; i < numVertices; i++) {\n      var offset = vertexOffsets[vertexOffset + i];\n      var mortonEncodedVertex = vertexBuffer[offset];\n      // TODO: refactor to use instance methods\n      var vertex =\n          ZOrderCurve.decode(\n              mortonEncodedVertex, mortonSettings.numBits, mortonSettings.coordinateShift);\n      vertices[i] = new Coordinate(vertex[0], vertex[1]);\n    }\n    if (closeLineString) {\n      vertices[vertices.length - 1] = vertices[0];\n    }\n\n    return vertices;\n  }\n\n  public static boolean containsPolygon(List<Integer> geometryTypes) {\n    return geometryTypes.stream()\n        .anyMatch(\n            geometryType ->\n                geometryType == GeometryType.POLYGON.ordinal()\n                    || geometryType == GeometryType.MULTIPOLYGON.ordinal());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/IntegerDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.decoder.vectorized.VectorizedDecodingUtils;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.MortonEncodedStreamMetadata;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.RleEncodedStreamMetadata;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\n\npublic class IntegerDecoder {\n\n  private IntegerDecoder() {}\n\n  public static List<Integer> decodeMortonStream(\n      byte[] data, IntWrapper offset, MortonEncodedStreamMetadata streamMetadata)\n      throws IOException {\n    int[] values;\n    if (streamMetadata.physicalLevelTechnique() == PhysicalLevelTechnique.FAST_PFOR) {\n      // TODO: numValues is not right if rle or delta rle is used -> add separate flag in\n      // StreamMetadata\n      values =\n          DecodingUtils.decodeFastPfor(\n              data, streamMetadata.numValues(), streamMetadata.byteLength(), offset);\n    } else if (streamMetadata.physicalLevelTechnique() == PhysicalLevelTechnique.VARINT) {\n      values = DecodingUtils.decodeVarints(data, offset, streamMetadata.numValues());\n    } else {\n      throw new IllegalArgumentException(\n          \"Specified physical level technique not yet supported: \"\n              + streamMetadata.physicalLevelTechnique());\n    }\n\n    return decodeMortonDelta(values, streamMetadata.numBits(), streamMetadata.coordinateShift());\n  }\n\n  private static List<Integer> decodeMortonDelta(int[] data, int numBits, int coordinateShift) {\n    var vertices = new ArrayList<Integer>(data.length * 2);\n    var previousMortonCode = 0;\n    for (var deltaCode : data) {\n      var mortonCode = previousMortonCode + deltaCode;\n      var vertex = decodeMortonCode(mortonCode, numBits, coordinateShift);\n      vertices.add(vertex[0]);\n      vertices.add(vertex[1]);\n      previousMortonCode = mortonCode;\n    }\n\n    return vertices;\n  }\n\n  private static List<Integer> decodeMortonCodes(int[] data, int numBits, int coordinateShift) {\n    var vertices = new ArrayList<Integer>(data.length * 2);\n    for (var mortonCode : data) {\n      var vertex = decodeMortonCode(mortonCode, numBits, coordinateShift);\n      vertices.add(vertex[0]);\n      vertices.add(vertex[1]);\n    }\n\n    return vertices;\n  }\n\n  private static int[] decodeMortonCode(int mortonCode, int numBits, int coordinateShift) {\n    int x = decodeMorton(mortonCode, numBits) - coordinateShift;\n    int y = decodeMorton(mortonCode >> 1, numBits) - coordinateShift;\n    return new int[] {x, y};\n  }\n\n  private static int decodeMorton(int code, int numBits) {\n    int coordinate = 0;\n    for (int i = 0; i < numBits; i++) {\n      coordinate |= (code & (1 << (2 * i))) >> i;\n    }\n    return coordinate;\n  }\n\n  public static List<Integer> decodeIntStream(\n      byte[] data, IntWrapper offset, StreamMetadata streamMetadata, boolean isSigned)\n      throws IOException {\n    int[] values;\n    if (streamMetadata.physicalLevelTechnique() == PhysicalLevelTechnique.FAST_PFOR) {\n      values =\n          DecodingUtils.decodeFastPfor(\n              data, streamMetadata.numValues(), streamMetadata.byteLength(), offset);\n    } else if (streamMetadata.physicalLevelTechnique() == PhysicalLevelTechnique.VARINT) {\n      values = DecodingUtils.decodeVarints(data, offset, streamMetadata.numValues());\n    } else {\n      throw new IllegalArgumentException(\n          \"Specified physical level technique not yet supported: \"\n              + streamMetadata.physicalLevelTechnique());\n    }\n\n    return decodeIntArray(values, streamMetadata, isSigned);\n  }\n\n  private static List<Integer> decodeIntArray(\n      int[] values, StreamMetadata streamMetadata, boolean isSigned) throws IOException {\n    switch (streamMetadata.logicalLevelTechnique1()) {\n      case DELTA:\n        if (streamMetadata.logicalLevelTechnique2().equals(LogicalLevelTechnique.RLE)) {\n          var rleMetadata = (RleEncodedStreamMetadata) streamMetadata;\n          values =\n              DecodingUtils.decodeUnsignedRLE(\n                  values, rleMetadata.runs(), rleMetadata.numRleValues());\n          return decodeZigZagDelta(values);\n        }\n        return decodeZigZagDelta(values);\n      case RLE:\n        {\n          var rleMetadata = (RleEncodedStreamMetadata) streamMetadata;\n          var decodedValues = decodeRLE(values, rleMetadata.runs(), rleMetadata.numRleValues());\n          return isSigned\n              ? decodeZigZag(decodedValues.stream().mapToInt(i -> i).toArray())\n              : decodedValues;\n        }\n      case NONE:\n        {\n          if (isSigned) {\n            return decodeZigZag(values);\n          }\n          return Arrays.stream(values).boxed().collect(Collectors.toList());\n        }\n      case COMPONENTWISE_DELTA:\n        VectorizedDecodingUtils.decodeComponentwiseDeltaVec2(values);\n        return Arrays.stream(values).boxed().collect(Collectors.toList());\n      case MORTON:\n        // TODO: zig-zag decode when morton second logical level technique\n        return decodeMortonCodes(\n            values,\n            ((MortonEncodedStreamMetadata) streamMetadata).numBits(),\n            ((MortonEncodedStreamMetadata) streamMetadata).coordinateShift());\n    }\n\n    throw new IllegalArgumentException(\n        \"The specified logical level technique is not supported for integers: \"\n            + streamMetadata.logicalLevelTechnique1());\n  }\n\n  public static List<Long> decodeLongStream(\n      byte[] data, IntWrapper offset, StreamMetadata streamMetadata, boolean isSigned) {\n    var values = DecodingUtils.decodeLongVarints(data, offset, streamMetadata.numValues());\n    return decodeLongArray(values, streamMetadata, isSigned);\n  }\n\n  private static List<Long> decodeLongArray(\n      long[] values, StreamMetadata streamMetadata, boolean isSigned) {\n    switch (streamMetadata.logicalLevelTechnique1()) {\n      case DELTA:\n        if (streamMetadata.logicalLevelTechnique2().equals(LogicalLevelTechnique.RLE)) {\n          var rleMetadata = (RleEncodedStreamMetadata) streamMetadata;\n          values =\n              DecodingUtils.decodeUnsignedRLE(\n                  values, rleMetadata.runs(), rleMetadata.numRleValues());\n          return decodeZigZagDelta(values);\n        }\n        return decodeZigZagDelta(values);\n      case RLE:\n        {\n          var rleMetadata = (RleEncodedStreamMetadata) streamMetadata;\n          var decodedValues = decodeRLE(values, rleMetadata.runs(), rleMetadata.numRleValues());\n          return isSigned\n              ? decodeZigZag(decodedValues.stream().mapToLong(i -> i).toArray())\n              : decodedValues;\n        }\n      case NONE:\n        {\n          if (isSigned) {\n            return decodeZigZag(values);\n          }\n          return Arrays.stream(values).boxed().collect(Collectors.toList());\n        }\n    }\n\n    throw new IllegalArgumentException(\n        \"The specified logical level technique is not supported for long integers: \"\n            + streamMetadata.logicalLevelTechnique1());\n  }\n\n  // TODO: quick and dirty -> write fast vectorized solution\n  private static List<Integer> decodeRLE(int[] data, int numRuns, int numRleValues) {\n    var values = new ArrayList<Integer>(numRleValues);\n    for (var i = 0; i < numRuns; i++) {\n      var run = data[i];\n      var value = data[i + numRuns];\n      for (var j = 0; j < run; j++) {\n        values.add(value);\n      }\n    }\n\n    return values;\n  }\n\n  // TODO: quick and dirty -> write fast vectorized solution\n  private static List<Long> decodeRLE(long[] data, int numRuns, int numRleValues) {\n    var values = new ArrayList<Long>(numRleValues);\n    for (var i = 0; i < numRuns; i++) {\n      var run = data[i];\n      var value = data[i + numRuns];\n      for (var j = 0; j < run; j++) {\n        values.add(value);\n      }\n    }\n\n    return values;\n  }\n\n  private static List<Integer> decodeZigZagDelta(int[] data) {\n    var values = new ArrayList<Integer>(data.length);\n    var previousValue = 0;\n    for (var zigZagDelta : data) {\n      var delta = DecodingUtils.decodeZigZag(zigZagDelta);\n      var value = previousValue + delta;\n      values.add(value);\n      previousValue = value;\n    }\n\n    return values;\n  }\n\n  private static List<Long> decodeZigZagDelta(long[] data) {\n    var values = new ArrayList<Long>(data.length);\n    var previousValue = 0L;\n    for (var zigZagDelta : data) {\n      var delta = DecodingUtils.decodeZigZag(zigZagDelta);\n      var value = previousValue + delta;\n      values.add(value);\n      previousValue = value;\n    }\n\n    return values;\n  }\n\n  private static List<Long> decodeZigZag(long[] data) {\n    var values = new ArrayList<Long>(data.length);\n    for (var zigZagDelta : data) {\n      var value = DecodingUtils.decodeZigZag(zigZagDelta);\n      values.add(value);\n    }\n    return values;\n  }\n\n  private static List<Integer> decodeZigZag(int[] data) {\n    var values = new ArrayList<Integer>(data.length);\n    for (var zigZagDelta : data) {\n      var value = DecodingUtils.decodeZigZag(zigZagDelta);\n      values.add(value);\n    }\n    return values;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/MltDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport com.google.common.io.CountingInputStream;\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.SequencedCollection;\nimport java.util.stream.Collectors;\nimport me.lemire.integercompression.IntWrapper;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.locationtech.jts.geom.Geometry;\nimport org.maplibre.mlt.converter.encodings.MltTypeMap;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.IndexedProperty;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MLTFeature;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.Property;\nimport org.maplibre.mlt.metadata.stream.StreamMetadataDecoder;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic class MltDecoder {\n\n  private MltDecoder() {}\n\n  private static Layer parseBasicMVTEquivalent(int layerSize, InputStream stream)\n      throws IOException {\n    try (var countStream = new CountingInputStream(stream)) {\n      final var metadataExtent = parseEmbeddedMetadata(countStream);\n      final var metadata = metadataExtent.getLeft();\n      final var tileExtent = metadataExtent.getRight();\n      final var bodySize = layerSize - countStream.getCount();\n      return decodeMltLayer(countStream.readNBytes((int) bodySize), metadata, tileExtent);\n    }\n  }\n\n  /** Decode an MLT tile with embedded metadata * */\n  public static MapLibreTile decodeMlTile(byte[] tileData) throws IOException {\n    final var layers = new ArrayList<Layer>();\n    try (final var stream = new ByteArrayInputStream(tileData)) {\n      while (stream.available() > 0) {\n        final var length = DecodingUtils.decodeVarint(stream);\n        final var tag = DecodingUtils.decodeVarintWithLength(stream);\n        final var bodySize = length - tag.getRight();\n        if (tag.getLeft() == 1) {\n          final var layer = parseBasicMVTEquivalent(bodySize, stream);\n          if (layer != null) {\n            layers.add(layer);\n          }\n        } else {\n          // Skip the remainder of this one\n          stream.skip(length - tag.getRight());\n        }\n      }\n    }\n    return new MapLibreTile(layers);\n  }\n\n  /** Decodes an MLT tile in a similar in-memory representation then MVT is using */\n  public static Layer decodeMltLayer(\n      byte[] tile, MltMetadata.FeatureTable layerMetadata, int tileExtent) throws IOException {\n    final var offset = new IntWrapper(0);\n    List<Long> ids = null;\n    Geometry[] geometries = null;\n    final var properties = new ArrayList<Map<String, Property>>();\n    for (var columnMetadata : layerMetadata.columns()) {\n      final var columnName = columnMetadata.getName();\n      final var hasStreamCount = MltTypeMap.Tag0x01.hasStreamCount(columnMetadata);\n      final var numStreams = hasStreamCount ? DecodingUtils.decodeVarints(tile, offset, 1)[0] : 0;\n      // TODO: add decoding of vector type to be compliant with the spec\n      // TODO: compare based on ids\n      if (MltTypeMap.Tag0x01.isID(columnMetadata)) {\n        BitSet presentStream = null;\n        int presentStreamSize = 0;\n        if (columnMetadata.isNullable()) {\n          final var presentStreamMetadata = StreamMetadataDecoder.decode(tile, offset);\n          presentStream =\n              DecodingUtils.decodeBooleanRle(\n                  tile,\n                  presentStreamMetadata.numValues(),\n                  presentStreamMetadata.byteLength(),\n                  offset);\n          presentStreamSize = presentStreamMetadata.numValues();\n        }\n\n        final var idDataStreamMetadata = StreamMetadataDecoder.decode(tile, offset);\n        List<Long> denseIds;\n        if (columnMetadata.field().type().scalarType().hasLongId()) {\n          denseIds = IntegerDecoder.decodeLongStream(tile, offset, idDataStreamMetadata, false);\n        } else {\n          denseIds =\n              IntegerDecoder.decodeIntStream(tile, offset, idDataStreamMetadata, false).stream()\n                  .mapToLong(i -> i)\n                  .boxed()\n                  .collect(Collectors.toList());\n        }\n\n        if (presentStream != null) {\n          // Expand the dense (non-null only) ID list into a sparse list with nulls\n          ids = new ArrayList<>(presentStreamSize);\n          int denseIdx = 0;\n          for (int i = 0; i < presentStreamSize; i++) {\n            if (presentStream.get(i)) {\n              ids.add(denseIds.get(denseIdx++));\n            } else {\n              ids.add(null);\n            }\n          }\n        } else {\n          ids = denseIds;\n        }\n      } else if (MltTypeMap.Tag0x01.isGeometry(columnMetadata)) {\n        assert hasStreamCount;\n        final var geometryColumn = GeometryDecoder.decodeGeometryColumn(tile, numStreams, offset);\n        geometries = GeometryDecoder.decodeGeometry(geometryColumn);\n      } else {\n        final var propertyColumn =\n            PropertyDecoder.decodePropertyColumn(tile, offset, columnMetadata, numStreams);\n        if (propertyColumn instanceof HashMap<?, ?>) {\n          @SuppressWarnings(\"unchecked\")\n          var p = ((Map<String, Object>) propertyColumn);\n          for (var a : p.entrySet()) {\n            final var key = a.getKey();\n            if (a.getValue() instanceof ArrayList<?>) {\n              @SuppressWarnings(\"unchecked\")\n              final var list = (ArrayList<Object>) a.getValue();\n              sizeList(properties, list);\n              final var prop = new IndexedProperty(columnMetadata.field().type(), key, list);\n              for (int i = 0; i < list.size(); i++) {\n                properties.get(i).merge(key, prop, MltDecoder::mergeFail);\n              }\n            }\n          }\n        } else if (propertyColumn instanceof SequencedCollection<?>) {\n          @SuppressWarnings(\"unchecked\")\n          final var list = (SequencedCollection<Object>) propertyColumn;\n          sizeList(properties, list);\n          final var prop = new IndexedProperty(columnMetadata.field().type(), columnName, list);\n          for (int i = 0; i < list.size(); i++) {\n            properties.get(i).merge(columnName, prop, MltDecoder::mergeFail);\n          }\n        } else {\n          throw new RuntimeException(\"Unexpected property result\");\n        }\n      }\n    }\n\n    return (geometries != null)\n        ? convertToLayer(ids, geometries, properties, layerMetadata, tileExtent)\n        : null;\n  }\n\n  private static void sizeList(\n      ArrayList<Map<String, Property>> properties, SequencedCollection<Object> list) {\n    if (properties.isEmpty()) {\n      for (int i = 0; i < list.size(); i++) {\n        properties.add(new HashMap<>());\n      }\n    } else if (properties.size() != list.size()) {\n      throw new RuntimeException(\"Feature count mismatch\");\n    }\n  }\n\n  private static Layer convertToLayer(\n      List<Long> ids,\n      Geometry[] geometries,\n      ArrayList<Map<String, Property>> properties,\n      MltMetadata.FeatureTable metadata,\n      int tileExtent) {\n    if (ids != null && geometries.length != ids.size()) {\n      System.out.println(\n          \"Warning, in convertToLayer the size of ids(\"\n              + ids.size()\n              + \"), geometries(\"\n              + geometries.length\n              + \"), are not equal for layer: \"\n              + metadata.name());\n    }\n    final var features = new ArrayList<Feature>(geometries.length);\n    final var builder = MLTFeature.builder();\n    for (var j = 0; j < geometries.length; j++) {\n      features.add(\n          builder\n              .index(j)\n              .id((ids != null) ? ids.get(j) : null)\n              .geometry(geometries[j])\n              .properties(properties.isEmpty() ? Map.of() : properties.get(j))\n              .build());\n    }\n\n    return new Layer(metadata.name(), features, tileExtent);\n  }\n\n  private static Property mergeFail(Property a, Property ignored) {\n    throw new RuntimeException(\"Duplicate property key: \" + a.getName());\n  }\n\n  private static MltMetadata.Column decodeColumn(InputStream stream) throws IOException {\n    final var typeCode = DecodingUtils.decodeVarint(stream);\n    var type = MltTypeMap.Tag0x01.decodeColumnType(typeCode);\n\n    String name = null;\n    if (MltTypeMap.Tag0x01.columnTypeHasName(typeCode)) {\n      name = DecodingUtils.decodeString(stream);\n    }\n\n    ArrayList<MltMetadata.Field> children = null;\n    if (MltTypeMap.Tag0x01.columnTypeHasChildren(typeCode)) {\n      final var childCount = DecodingUtils.decodeVarint(stream);\n      if (childCount > 0) {\n        children = new ArrayList<MltMetadata.Field>(childCount);\n        for (var i = 0; i < childCount; ++i) {\n          children.add(decodeColumn(stream).field());\n        }\n      }\n      type =\n          new MltMetadata.FieldType(\n              new MltMetadata.ComplexField(type.complexType().physicalType(), children),\n              type.isNullable());\n    }\n\n    return new MltMetadata.Column(new MltMetadata.Field(type, name));\n  }\n\n  public static Pair<MltMetadata.FeatureTable, Integer> parseEmbeddedMetadata(InputStream stream)\n      throws IOException {\n    final var table = new MltMetadata.FeatureTable(DecodingUtils.decodeString(stream));\n    final var extent = DecodingUtils.decodeVarint(stream);\n\n    final var columnCount = DecodingUtils.decodeVarint(stream);\n    for (int i = 0; i < columnCount; ++i) {\n      table.columns().add(decodeColumn(stream));\n    }\n    return Pair.of(table, extent);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/PropertyDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport jakarta.annotation.Nullable;\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.metadata.stream.StreamMetadataDecoder;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic class PropertyDecoder {\n\n  private PropertyDecoder() {}\n\n  /// Use present bits to reconstitute the original list with null values, if appropriate\n  private static <T> List<T> unpack(\n      List<T> dataStream, @Nullable BitSet presentBits, int numPresentBits) {\n    if (presentBits == null) {\n      return dataStream;\n    }\n    final ArrayList<T> outValues = new ArrayList<>(presentBits.size());\n    var counter = 0;\n    for (var i = 0; i < numPresentBits; i++) {\n      outValues.add(presentBits.get(i) ? dataStream.get(counter++) : null);\n    }\n    return outValues;\n  }\n\n  ///  Special case for boolean columns because `BitSet` is not compatible with `List`\n  private static List<Boolean> unpack(\n      BitSet dataStream, int dataStreamSize, @Nullable BitSet presentBits, int numPresentBits) {\n    final var numValues = (presentBits != null) ? numPresentBits : dataStreamSize;\n    final ArrayList<Boolean> booleanValues = new ArrayList<>(numValues);\n    var counter = 0;\n    for (var i = 0; i < numValues; i++) {\n      booleanValues.add(\n          (presentBits == null || presentBits.get(i)) ? dataStream.get(counter++) : null);\n    }\n    return booleanValues;\n  }\n\n  private static Object decodeScalarPropertyColumn(\n      byte[] data,\n      IntWrapper offset,\n      MltMetadata.ScalarField scalarType,\n      boolean nullable,\n      int numStreams)\n      throws IOException {\n    final BitSet presentStream;\n    final int presentStreamSize;\n    if (nullable) {\n      final var presentStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n      presentStream =\n          DecodingUtils.decodeBooleanRle(\n              data, presentStreamMetadata.numValues(), presentStreamMetadata.byteLength(), offset);\n      presentStreamSize = presentStreamMetadata.numValues();\n      numStreams -= 1;\n    } else {\n      presentStream = null;\n      presentStreamSize = 0;\n    }\n\n    return switch (scalarType.physicalType()) {\n      case BOOLEAN -> {\n        final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        final var dataStream =\n            DecodingUtils.decodeBooleanRle(\n                data, dataStreamMetadata.numValues(), dataStreamMetadata.byteLength(), offset);\n        yield unpack(dataStream, dataStreamMetadata.numValues(), presentStream, presentStreamSize);\n      }\n      case UINT_32, INT_32 -> {\n        final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        final var signed = (scalarType.physicalType() == MltMetadata.ScalarType.INT_32);\n        final var dataStream =\n            IntegerDecoder.decodeIntStream(data, offset, dataStreamMetadata, signed);\n\n        // otherwise, we have u32.MAX -> -1\n        final var values =\n            signed\n                ? dataStream\n                : dataStream.stream()\n                    .map(i -> i == null ? null : Integer.toUnsignedLong(i))\n                    .toList();\n\n        yield unpack(values, presentStream, presentStreamSize);\n      }\n      case UINT_64, INT_64 -> {\n        final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        final var signed = (scalarType.physicalType() == MltMetadata.ScalarType.INT_64);\n        final var dataStream =\n            IntegerDecoder.decodeLongStream(data, offset, dataStreamMetadata, signed);\n\n        // otherwise, we have u64.MAX -> -1\n        final var values =\n            signed\n                ? dataStream\n                : dataStream.stream().map(i -> i == null ? null : toUnsignedBigInteger(i)).toList();\n\n        yield unpack(values, presentStream, presentStreamSize);\n      }\n      case FLOAT -> {\n        final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        final var dataStream = FloatDecoder.decodeFloatStream(data, offset, dataStreamMetadata);\n        yield unpack(dataStream, presentStream, presentStreamSize);\n      }\n      case DOUBLE -> {\n        final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        final var dataStream = DoubleDecoder.decodeDoubleStream(data, offset, dataStreamMetadata);\n        yield unpack(dataStream, presentStream, presentStreamSize);\n      }\n      case STRING -> {\n        final var strValues =\n            StringDecoder.decode(data, offset, numStreams, presentStream, presentStreamSize);\n        yield strValues.getRight();\n      }\n      case UINT_8, UNRECOGNIZED, INT_8 ->\n          throw new IllegalArgumentException(\n              \"The specified data type for the field is currently not supported: \" + scalarType);\n    };\n  }\n\n  private static BigInteger toUnsignedBigInteger(Long value) {\n    if (value >= 0) {\n      return BigInteger.valueOf(value);\n    }\n    return BigInteger.valueOf(value).add(BigInteger.ONE.shiftLeft(64));\n  }\n\n  public static Object decodePropertyColumn(\n      byte[] data, IntWrapper offset, MltMetadata.Column column, int numStreams)\n      throws IOException {\n    if (column.isScalar()) {\n      return decodeScalarPropertyColumn(\n          data, offset, column.field().type().scalarType(), column.isNullable(), numStreams);\n    }\n\n    /* Handle struct which currently only supports strings as nested fields for supporting shared dictionary encoding */\n    if (numStreams > 1) {\n      return StringDecoder.decodeSharedDictionary(data, offset, column).getRight();\n    }\n\n    // var presentStreamMetadata = StreamMetadata.decode(data, offset);\n    // var presentStream = DecodingUtils.decodeBooleanRle(data, presentStreamMetadata.numValues(),\n    // presentStreamMetadata.byteLength(), offset);\n    // TODO: process present stream\n    // var values = StringDecoder.decodeSharedDictionary(data, offset, fieldMetadata);\n    throw new IllegalArgumentException(\"Present stream currently not supported for Structs.\");\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/StringDecoder.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport jakarta.annotation.Nullable;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport me.lemire.integercompression.IntWrapper;\nimport org.apache.commons.lang3.NotImplementedException;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.maplibre.mlt.converter.encodings.fsst.FsstEncoder;\nimport org.maplibre.mlt.metadata.stream.DictionaryType;\nimport org.maplibre.mlt.metadata.stream.LengthType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadataDecoder;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic final class StringDecoder {\n\n  private StringDecoder() {}\n\n  public static Triple<HashMap<String, Integer>, HashMap<String, BitSet>, Map<String, List<String>>>\n      decodeSharedDictionary(byte[] data, IntWrapper offset, MltMetadata.Column column)\n          throws IOException {\n    List<Integer> dictionaryLengthStream = null;\n    byte[] dictionaryStream = null;\n    List<Integer> symbolLengthStream = null;\n    byte[] symbolTableStream = null;\n\n    // TODO: refactor to be spec compliant -> start by decoding the FieldMetadata, StreamMetadata\n    // and PresentStream\n    boolean dictionaryStreamDecoded = false;\n    while (!dictionaryStreamDecoded) {\n      var streamMetadata = StreamMetadataDecoder.decode(data, offset);\n      switch (streamMetadata.physicalStreamType()) {\n        case LENGTH:\n          {\n            if (LengthType.DICTIONARY.equals(streamMetadata.logicalStreamType().lengthType())) {\n              dictionaryLengthStream =\n                  IntegerDecoder.decodeIntStream(data, offset, streamMetadata, false);\n            } else {\n              symbolLengthStream =\n                  IntegerDecoder.decodeIntStream(data, offset, streamMetadata, false);\n            }\n            break;\n          }\n        case DATA:\n          {\n            // TODO: fix -> only shared should be allowed in that case\n            if (DictionaryType.SINGLE.equals(streamMetadata.logicalStreamType().dictionaryType())\n                || DictionaryType.SHARED.equals(\n                    streamMetadata.logicalStreamType().dictionaryType())) {\n              dictionaryStream =\n                  Arrays.copyOfRange(\n                      data, offset.get(), offset.get() + streamMetadata.byteLength());\n              offset.set(offset.get() + streamMetadata.byteLength());\n              dictionaryStreamDecoded = true;\n            } else {\n              symbolTableStream =\n                  Arrays.copyOfRange(\n                      data, offset.get(), offset.get() + streamMetadata.byteLength());\n              offset.set(offset.get() + streamMetadata.byteLength());\n            }\n            break;\n          }\n      }\n    }\n\n    List<String> dictionary;\n    if (symbolLengthStream != null && symbolTableStream != null && dictionaryLengthStream != null) {\n      var decompressedLength = dictionaryLengthStream.stream().mapToInt(i -> i).sum();\n      var utf8Values =\n          FsstEncoder.decode(\n              symbolTableStream,\n              symbolLengthStream.stream().mapToInt(i -> i).toArray(),\n              dictionaryStream,\n              decompressedLength);\n      dictionary = decodeDictionary(dictionaryLengthStream, utf8Values);\n    } else if (dictionaryLengthStream != null) {\n      dictionary = decodeDictionary(dictionaryLengthStream, dictionaryStream);\n    } else {\n      throw new NotImplementedException(\"Expected streams missing in shared dictionary decoding\");\n    }\n\n    var presentStreams = new HashMap<String, BitSet>();\n    var numValues = new HashMap<String, Integer>();\n    var values = new HashMap<String, List<String>>();\n    for (var childField : column.field().type().complexType().children()) {\n      var numStreams = DecodingUtils.decodeVarints(data, offset, 1)[0];\n      if (childField.type().scalarType() == null\n          || childField.type().scalarType().physicalType() != MltMetadata.ScalarType.STRING) {\n        throw new IllegalArgumentException(\n            \"Currently only scalar string fields are implemented for a struct.\");\n      }\n      if ((numStreams > 1) != childField.type().isNullable()) {\n        throw new IllegalArgumentException(\n            \"The number of streams for the child field \"\n                + childField.name()\n                + \" does not match its nullability.\");\n      }\n\n      @Nullable BitSet presentStream = null;\n      int presentCount = 0;\n      if (childField.type().isNullable()) {\n        final var presentStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n        presentCount = presentStreamMetadata.numValues();\n        presentStream =\n            DecodingUtils.decodeBooleanRle(\n                data, presentCount, presentStreamMetadata.byteLength(), offset);\n        numStreams -= 1;\n      }\n\n      final var dataStreamMetadata = StreamMetadataDecoder.decode(data, offset);\n      final var dataReferenceStream =\n          IntegerDecoder.decodeIntStream(data, offset, dataStreamMetadata, false);\n\n      final var valueCount = (presentStream != null) ? presentCount : dataReferenceStream.size();\n      final var propertyValues = new ArrayList<String>(valueCount);\n      var counter = 0;\n      for (var i = 0; i < valueCount; i++) {\n        final var present = (presentStream == null) || presentStream.get(i);\n        propertyValues.add(present ? dictionary.get(dataReferenceStream.get(counter++)) : null);\n      }\n\n      final var columnName = column.getName() + childField.name();\n      numValues.put(columnName, valueCount);\n      presentStreams.put(columnName, presentStream);\n      values.put(columnName, propertyValues);\n    }\n\n    return Triple.of(numValues, presentStreams, values);\n  }\n\n  private static List<String> decodeDictionary(List<Integer> lengthStream, byte[] utf8Values) {\n    // var strValues = new String(utf8Values, StandardCharsets.UTF_8);\n    var dictionary = new ArrayList<String>();\n    var dictionaryOffset = 0;\n    for (var length : lengthStream) {\n      // var value = strValues.substring(dictionaryOffset, dictionaryOffset + length);\n      var value = Arrays.copyOfRange(utf8Values, dictionaryOffset, dictionaryOffset + length);\n      dictionary.add(new String(value, StandardCharsets.UTF_8));\n      dictionaryOffset += length;\n    }\n\n    return dictionary;\n  }\n\n  public static Triple<Integer, BitSet, List<String>> decode(\n      byte[] data,\n      IntWrapper offset,\n      int numStreams,\n      @Nullable BitSet presentStream,\n      int presentCount)\n      throws IOException {\n    /*\n     * String column layouts:\n     * -> plain -> present, length, data\n     * -> dictionary -> present, length, dictionary, data\n     * -> fsst dictionary -> symbolTable, symbolLength, dictionary, length, present, data\n     * */\n\n    List<Integer> dictionaryLengthStream = null;\n    List<Integer> offsetStream = null;\n    byte[] dictionaryStream = null;\n    List<Integer> symbolLengthStream = null;\n    byte[] symbolTableStream = null;\n    for (var i = 0; i < numStreams; i++) {\n      final var streamMetadata = StreamMetadataDecoder.decode(data, offset);\n      switch (streamMetadata.physicalStreamType()) {\n        case OFFSET:\n          {\n            offsetStream = IntegerDecoder.decodeIntStream(data, offset, streamMetadata, false);\n            break;\n          }\n        case LENGTH:\n          {\n            var ls = IntegerDecoder.decodeIntStream(data, offset, streamMetadata, false);\n            if (LengthType.DICTIONARY.equals(streamMetadata.logicalStreamType().lengthType())) {\n              dictionaryLengthStream = ls;\n            } else {\n              symbolLengthStream = ls;\n            }\n\n            break;\n          }\n        case DATA:\n          {\n            var ds =\n                Arrays.copyOfRange(data, offset.get(), offset.get() + streamMetadata.byteLength());\n            offset.add(streamMetadata.byteLength());\n            if (DictionaryType.SINGLE.equals(streamMetadata.logicalStreamType().dictionaryType())) {\n              dictionaryStream = ds;\n            } else {\n              symbolTableStream = ds;\n            }\n            break;\n          }\n      }\n    }\n\n    if (symbolTableStream != null && symbolLengthStream != null && dictionaryLengthStream != null) {\n      final var decompressedLength = dictionaryLengthStream.stream().mapToInt(i -> i).sum();\n      final var utf8Values =\n          FsstEncoder.decode(\n              symbolTableStream,\n              symbolLengthStream.stream().mapToInt(i -> i).toArray(),\n              dictionaryStream,\n              decompressedLength);\n      final var strings =\n          decodeDictionary(\n              presentStream, dictionaryLengthStream, utf8Values, offsetStream, presentCount);\n      return Triple.of(strings.size(), presentStream, strings);\n    } else if (dictionaryStream != null && dictionaryLengthStream != null) {\n      final var strings =\n          decodeDictionary(\n              presentStream, dictionaryLengthStream, dictionaryStream, offsetStream, presentCount);\n      return Triple.of(strings.size(), presentStream, strings);\n    } else {\n      final var strings =\n          decodePlain(presentStream, symbolLengthStream, symbolTableStream, presentCount);\n      return Triple.of(strings.size(), presentStream, strings);\n    }\n  }\n\n  private static List<String> decodePlain(\n      @Nullable BitSet presentStream,\n      List<Integer> lengthStream,\n      byte[] utf8Values,\n      int presentCount) {\n    final var numValues = (presentStream != null) ? presentCount : lengthStream.size();\n    final var decodedValues = new ArrayList<String>(numValues);\n    var lengthOffset = 0;\n    var strOffset = 0;\n    for (var i = 0; i < numValues; i++) {\n      final var present = (presentStream == null) || presentStream.get(i);\n      if (present) {\n        final var length = lengthStream.get(lengthOffset++);\n        final var value = new String(utf8Values, strOffset, length, StandardCharsets.UTF_8);\n        decodedValues.add(value);\n        strOffset += length;\n      } else {\n        decodedValues.add(null);\n      }\n    }\n\n    return decodedValues;\n  }\n\n  private static List<String> decodeDictionary(\n      @Nullable BitSet presentStream,\n      List<Integer> lengthStream,\n      byte[] utf8Values,\n      List<Integer> dictionaryOffsets,\n      int presentCount) {\n    final var dictionary = new ArrayList<String>();\n    var dictionaryOffset = 0;\n    for (var length : lengthStream) {\n      final var value =\n          new String(\n              Arrays.copyOfRange(utf8Values, dictionaryOffset, dictionaryOffset + length),\n              StandardCharsets.UTF_8);\n      dictionary.add(value);\n      dictionaryOffset += length;\n    }\n\n    final var numValues = (presentStream != null) ? presentCount : dictionaryOffsets.size();\n    final var values = new ArrayList<String>(numValues);\n    var offset = 0;\n    for (var i = 0; i < numValues; i++) {\n      final var present = (presentStream == null) || presentStream.get(i);\n      values.add(present ? dictionary.get(dictionaryOffsets.get(offset++)) : null);\n    }\n\n    return values;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/decoder/vectorized/VectorizedDecodingUtils.java",
    "content": "package org.maplibre.mlt.decoder.vectorized;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.IntBuffer;\nimport me.lemire.integercompression.Composition;\nimport me.lemire.integercompression.FastPFOR;\nimport me.lemire.integercompression.IntWrapper;\nimport me.lemire.integercompression.IntegerCODEC;\nimport me.lemire.integercompression.VariableByte;\n\n/* the redundant implementations in this class are mainly to avoid branching and therefore speed up the decoding */\npublic class VectorizedDecodingUtils {\n  private VectorizedDecodingUtils() {}\n\n  private static IntegerCODEC ic;\n\n  public static IntBuffer decodeFastPfor(\n      byte[] buffer, int numValues, int byteLength, IntWrapper offset) {\n    if (ic == null) {\n      ic = new Composition(new FastPFOR(), new VariableByte());\n    }\n\n    /* Create a vectorized conversion from the ByteBuffer to the IntBuffer */\n    // TODO: get rid of that conversion\n    IntBuffer intBuf =\n        ByteBuffer.wrap(buffer, offset.get(), byteLength).order(ByteOrder.BIG_ENDIAN).asIntBuffer();\n    var bufferSize = (int) Math.ceil(byteLength / 4d);\n    int[] intValues = new int[bufferSize];\n    for (var i = 0; i < intValues.length; i++) {\n      intValues[i] = intBuf.get(i);\n    }\n\n    int[] decodedValues = new int[numValues];\n    ic.uncompress(intValues, new IntWrapper(0), intValues.length, decodedValues, new IntWrapper(0));\n\n    offset.add(byteLength);\n    return IntBuffer.wrap(decodedValues);\n  }\n\n  /* Delta encoding  ------------------------------------------------------------------------------*/\n\n  /*\n   * In place decoding of the zigzag delta encoded Vec2.\n   * Inspired by https://github.com/lemire/JavaFastPFOR/blob/master/src/main/java/me/lemire/integercompression/differential/Delta.java\n   */\n  public static void decodeComponentwiseDeltaVec2(int[] data) {\n    data[0] = (data[0] >>> 1) ^ ((data[0] << 31) >> 31);\n    data[1] = (data[1] >>> 1) ^ ((data[1] << 31) >> 31);\n    int sz0 = data.length / 4 * 4;\n    int i = 2;\n    if (sz0 >= 4) {\n      for (; i < sz0 - 4; i += 4) {\n        var x1 = data[i];\n        var y1 = data[i + 1];\n        var x2 = data[i + 2];\n        var y2 = data[i + 3];\n\n        data[i] = ((x1 >>> 1) ^ ((x1 << 31) >> 31)) + data[i - 2];\n        data[i + 1] = ((y1 >>> 1) ^ ((y1 << 31) >> 31)) + data[i - 1];\n        data[i + 2] = ((x2 >>> 1) ^ ((x2 << 31) >> 31)) + data[i];\n        data[i + 3] = ((y2 >>> 1) ^ ((y2 << 31) >> 31)) + data[i + 1];\n      }\n    }\n\n    for (; i != data.length; i += 2) {\n      data[i] = ((data[i] >>> 1) ^ ((data[i] << 31) >> 31)) + data[i - 2];\n      data[i + 1] = ((data[i + 1] >>> 1) ^ ((data[i + 1] << 31) >> 31)) + data[i - 1];\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/json/Json.java",
    "content": "package org.maplibre.mlt.json;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.Strictness;\nimport com.google.gson.reflect.TypeToken;\nimport java.lang.reflect.Type;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.SortedMap;\nimport java.util.TreeMap;\nimport java.util.stream.Collectors;\nimport java.util.stream.StreamSupport;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.io.geojson.GeoJsonWriter;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.data.Property;\n\n/** Utility for converting MVT and MLT tiles to JSON and GeoJSON. */\npublic final class Json {\n  // GeoJSON does not support non-numeric floats; use Rust-style string tokens for\n  // cross-implementation consistency.\n  private static final String F32_NAN = \"f32::NAN\";\n  private static final String F32_INFINITY = \"f32::INFINITY\";\n  private static final String F32_NEG_INFINITY = \"f32::NEG_INFINITY\";\n  private static final String F64_NAN = \"f64::NAN\";\n  private static final String F64_INFINITY = \"f64::INFINITY\";\n  private static final String F64_NEG_INFINITY = \"f64::NEG_INFINITY\";\n\n  private static final Type linkedHashMapType =\n      new TypeToken<LinkedHashMap<String, Object>>() {}.getType();\n\n  private Json() {}\n\n  /**\n   * Convert a MapboxVectorTile to a JSON string with the same structure as the MVT spec. The\n   * geometry is represented as a WKT string for comparison with MLT output. Note that this is not\n   * GeoJSON and is intended for testing/debugging purposes only.\n   *\n   * @param tile the tile to convert\n   * @param pretty if true, the output will be pretty-printed, otherwise, it will be compact\n   * @return a JSON string representing the tile\n   */\n  public static String toJson(MapboxVectorTile tile, boolean pretty) {\n    return createGson(pretty).toJson(toJsonObjects(tile));\n  }\n\n  /**\n   * Convert a MapLibreTile to a JSON string with the same structure as the MLT spec. The geometry\n   * is represented as a WKT string for comparison with MVT output. Note that this is not GeoJSON\n   * and is intended for testing/debugging purposes only.\n   *\n   * @param tile the tile to convert\n   * @param pretty if true, the output will be pretty-printed, otherwise, it will be compact\n   * @return a JSON string representing the tile\n   */\n  public static String toJson(MapLibreTile tile, boolean pretty) {\n    return createGson(pretty).toJson(toJsonObjects(tile));\n  }\n\n  /**\n   * Convert a MapLibreTile to a GeoJSON string. The layer name and tile extent are included as\n   * properties with keys \"_layer\" and \"_extent\", respectively, for testing/debugging purposes.\n   *\n   * @param tile the tile to convert\n   * @param pretty if true, the output will be pretty-printed, otherwise, it will be compact\n   * @return a GeoJSON string representing the tile\n   */\n  public static String toGeoJson(MapLibreTile tile, boolean pretty) {\n    final var gson = createGson(pretty);\n    return gson.toJson(toGeoJsonObjects(tile, gson));\n  }\n\n  private static Gson createGson(boolean pretty) {\n    final var builder =\n        new GsonBuilder()\n            .disableJdkUnsafe()\n            .disableHtmlEscaping()\n            // not .serializeNulls()\n            .serializeSpecialFloatingPointValues()\n            .setStrictness(Strictness.STRICT);\n    return (pretty ? builder.setPrettyPrinting() : builder).create();\n  }\n\n  private static Object floatToken(Float value) {\n    if (value.isNaN()) {\n      return F32_NAN;\n    } else if (value == Float.POSITIVE_INFINITY) {\n      return F32_INFINITY;\n    } else if (value == Float.NEGATIVE_INFINITY) {\n      return F32_NEG_INFINITY;\n    }\n    return value;\n  }\n\n  private static Object doubleToken(Double value) {\n    if (value.isNaN()) {\n      return F64_NAN;\n    } else if (value == Double.POSITIVE_INFINITY) {\n      return F64_INFINITY;\n    } else if (value == Double.NEGATIVE_INFINITY) {\n      return F64_NEG_INFINITY;\n    }\n    return value;\n  }\n\n  /** Recursively replace Float/Double NaN and +/-Infinity with GeoJSON string tokens. */\n  private static Object floatsAsStrings(Object obj) {\n    return switch (obj) {\n      case Float value -> floatToken(value);\n      case Double value -> doubleToken(value);\n      case Iterable<?> iterable ->\n          StreamSupport.stream(iterable.spliterator(), false).map(Json::floatsAsStrings).toList();\n      case Map<?, ?> map ->\n          map.entrySet().stream()\n              .collect(\n                  Collectors.toMap(\n                      Map.Entry::getKey,\n                      entry -> floatsAsStrings(entry.getValue()),\n                      Json::failOnDuplicate,\n                      LinkedHashMap::new));\n      default -> obj;\n    };\n  }\n\n  private static Map<String, Object> toJsonObjects(MapLibreTile mlTile) {\n    return Map.of(\"layers\", mlTile.getLayerStream().map(Json::toJson).toList());\n  }\n\n  private static Map<String, Object> toJson(Layer layer) {\n    final var map = new LinkedHashMap<String, Object>();\n    map.put(\"name\", layer.name());\n    map.put(\"extent\", layer.tileExtent());\n    map.put(\"features\", layer.features().stream().map(Json::toJson).toList());\n    return map;\n  }\n\n  private static Map<String, Object> toJson(Feature feature) {\n    final var featureMap = new LinkedHashMap<String, Object>();\n    if (feature.hasId()) {\n      featureMap.put(\"id\", feature.getId());\n    }\n    featureMap.put(\"geometry\", feature.getGeometry().toString());\n\n    // Keep non-null properties only to facilitate direct comparison with MVT output.\n    final var propertyMap =\n        feature\n            .getPropertyStream()\n            .filter(entry -> entry.getValue(feature.getIndex()) != null)\n            .collect(\n                Collectors.toMap(\n                    Property::getName,\n                    p -> p.getValue(feature.getIndex()),\n                    Json::failOnDuplicate,\n                    LinkedHashMap::new));\n\n    featureMap.put(\"properties\", propertyMap);\n    return featureMap;\n  }\n\n  public static Map<String, Object> toGeoJsonObjects(MapLibreTile mlTile, Gson gson) {\n    final var featureCollectionMap = new LinkedHashMap<String, Object>();\n    featureCollectionMap.put(\"type\", \"FeatureCollection\");\n    featureCollectionMap.put(\n        \"features\",\n        mlTile\n            .getLayerStream()\n            .flatMap(\n                layer ->\n                    layer.features().stream()\n                        .map(feature -> featureToGeoJson(layer, feature, gson)))\n            .toList());\n    return featureCollectionMap;\n  }\n\n  private static Map<String, Object> featureToGeoJson(Layer layer, Feature feature, Gson gson) {\n    final var featureMap = new LinkedHashMap<String, Object>();\n    featureMap.put(\"type\", \"Feature\");\n    if (feature.hasId()) {\n      featureMap.put(\"id\", feature.getId());\n    }\n\n    final var props = getSortedNonNullProperties(feature);\n    props.put(\"_layer\", layer.name());\n    props.put(\"_extent\", layer.tileExtent());\n    featureMap.put(\"properties\", floatsAsStrings(props));\n\n    final var geom = feature.getGeometry();\n    featureMap.put(\"geometry\", geom == null ? null : geometryToGeoJson(geom, gson));\n    return featureMap;\n  }\n\n  private static Object failOnDuplicate(Object a, Object b) {\n    throw new IllegalStateException(\"Duplicate key\");\n  }\n\n  private static SortedMap<String, Object> getSortedNonNullProperties(Feature feature) {\n    return feature\n        .getPropertyStream()\n        .filter(entry -> entry.getValue(feature.getIndex()) != null)\n        .collect(\n            Collectors.toMap(\n                Property::getName,\n                p -> p.getValue(feature.getIndex()),\n                Json::failOnDuplicate,\n                TreeMap::new));\n  }\n\n  private static Map<String, Object> geometryToGeoJson(Geometry geometry, Gson gson) {\n    var writer = new GeoJsonWriter();\n    writer.setEncodeCRS(false);\n\n    final LinkedHashMap<String, Object> map =\n        gson.fromJson(writer.write(geometry), linkedHashMapType);\n\n    if (map.containsKey(\"coordinates\")) {\n      map.put(\"coordinates\", intifyCoordinates(map.get(\"coordinates\")));\n    }\n    return map;\n  }\n\n  /** Recursively convert whole-number doubles to longs inside a coordinates structure. */\n  private static Object intifyCoordinates(Object obj) {\n    if (obj instanceof Iterable<?> list) {\n      return StreamSupport.stream(list.spliterator(), false).map(Json::intifyCoordinates).toList();\n    }\n\n    if (obj instanceof Double value\n        && value == Math.floor(value)\n        && !value.isInfinite()\n        && !value.isNaN()) {\n      return value.longValue();\n    }\n\n    return obj;\n  }\n\n  private static Map<String, Object> toJsonObjects(MapboxVectorTile mvTile) {\n    return Map.of(\"layers\", mvTile.getLayerStream().map(Json::toJson).toList());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/DictionaryType.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum DictionaryType {\n  NONE,\n  SINGLE,\n  SHARED,\n  VERTEX,\n  MORTON,\n  FSST\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/LengthType.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum LengthType {\n  VAR_BINARY,\n  GEOMETRIES,\n  PARTS,\n  RINGS,\n  TRIANGLES,\n  SYMBOL,\n  DICTIONARY\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/LogicalLevelTechnique.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum LogicalLevelTechnique {\n  NONE,\n  DELTA,\n  COMPONENTWISE_DELTA,\n  RLE,\n  MORTON,\n  /* Pseudodecimal Encoding of floats -> only for the exponent integer part an additional logical level technique is used.\n   *  Both exponent and significant parts are encoded with the same physical level technique */\n  PDE;\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/LogicalStreamType.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic class LogicalStreamType {\n  private DictionaryType dictionaryType;\n  private OffsetType offsetType;\n  private LengthType lengthType;\n\n  public LogicalStreamType(DictionaryType dictionaryType) {\n    this.dictionaryType = dictionaryType;\n  }\n\n  public LogicalStreamType(OffsetType offsetType) {\n    this.offsetType = offsetType;\n  }\n\n  public LogicalStreamType(LengthType lengthType) {\n    this.lengthType = lengthType;\n  }\n\n  public DictionaryType dictionaryType() {\n    return this.dictionaryType;\n  }\n\n  public OffsetType offsetType() {\n    return this.offsetType;\n  }\n\n  public LengthType lengthType() {\n    return this.lengthType;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/MortonEncodedStreamMetadata.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.decoder.DecodingUtils;\n\npublic class MortonEncodedStreamMetadata extends StreamMetadata {\n  private final int numBits;\n  private final int coordinateShift;\n\n  // TODO: refactor -> use builder pattern\n  public MortonEncodedStreamMetadata(\n      PhysicalStreamType physicalStreamType,\n      LogicalStreamType logicalStreamType,\n      LogicalLevelTechnique logicalLevelTechnique1,\n      LogicalLevelTechnique logicalLevelTechnique2,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      int numValues,\n      int byteLength,\n      int numBits,\n      int coordinateShift) {\n    super(\n        physicalStreamType,\n        logicalStreamType,\n        logicalLevelTechnique1,\n        logicalLevelTechnique2,\n        physicalLevelTechnique,\n        numValues,\n        byteLength);\n    this.numBits = numBits;\n    this.coordinateShift = coordinateShift;\n  }\n\n  public ArrayList<byte[]> encode() throws IOException {\n    final var result = super.encode();\n    result.add(EncodingUtils.encodeVarints(new int[] {numBits, coordinateShift}, false, false));\n    return result;\n  }\n\n  public static MortonEncodedStreamMetadata decodePartial(\n      StreamMetadata streamMetadata, byte[] tile, IntWrapper offset) throws IOException {\n    var mortonInfo = DecodingUtils.decodeVarints(tile, offset, 2);\n    return new MortonEncodedStreamMetadata(\n        streamMetadata.physicalStreamType(),\n        streamMetadata.logicalStreamType(),\n        streamMetadata.logicalLevelTechnique1(),\n        streamMetadata.logicalLevelTechnique2(),\n        streamMetadata.physicalLevelTechnique(),\n        streamMetadata.numValues(),\n        streamMetadata.byteLength(),\n        mortonInfo[0],\n        mortonInfo[1]);\n  }\n\n  public int numBits() {\n    return this.numBits;\n  }\n\n  public int coordinateShift() {\n    return this.coordinateShift;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/OffsetType.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum OffsetType {\n  VERTEX,\n  INDEX,\n  STRING,\n  KEY\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/PhysicalLevelTechnique.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum PhysicalLevelTechnique {\n  NONE,\n  /* Preferred option, tends to produce the best compression ratio and decoding performance.\n   * But currently only limited to 32 bit integer. */\n  FAST_PFOR,\n  /* Can produce better results in combination with a heavyweight compression scheme like Gzip.\n   *  Simple compression scheme where the decoder are easier to implement compared to FastPfor.*/\n  VARINT\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/PhysicalStreamType.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\npublic enum PhysicalStreamType {\n  PRESENT,\n  DATA,\n  OFFSET,\n  LENGTH\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/RleEncodedStreamMetadata.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.decoder.DecodingUtils;\n\npublic class RleEncodedStreamMetadata extends StreamMetadata {\n  int runs;\n  int numRleValues;\n\n  // TODO: refactor -> use builder pattern\n\n  /**\n   * Only used for RLE encoded integer values. Not needed for rle encoded boolean and byte values.\n   *\n   * @param numValues After LogicalLevelTechnique was applied -> numRuns + numValues\n   * @param runs Length of the runs array\n   * @param numRleValues Used for pre-allocating the arrays on the client for faster decoding\n   */\n  public RleEncodedStreamMetadata(\n      PhysicalStreamType physicalStreamType,\n      LogicalStreamType logicalStreamType,\n      LogicalLevelTechnique logicalLevelTechnique1,\n      LogicalLevelTechnique logicalLevelTechnique2,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      int numValues,\n      int byteLength,\n      int runs,\n      int numRleValues) {\n    super(\n        physicalStreamType,\n        logicalStreamType,\n        logicalLevelTechnique1,\n        logicalLevelTechnique2,\n        physicalLevelTechnique,\n        numValues,\n        byteLength);\n    this.runs = runs;\n    this.numRleValues = numRleValues;\n  }\n\n  public ArrayList<byte[]> encode() throws IOException {\n    final var result = super.encode();\n    result.add(EncodingUtils.encodeVarints(new int[] {runs, numRleValues}, false, false));\n    return result;\n  }\n\n  public static RleEncodedStreamMetadata decodePartial(\n      StreamMetadata streamMetadata, byte[] tile, IntWrapper offset) throws IOException {\n    var rleInfo = DecodingUtils.decodeVarints(tile, offset, 2);\n    return new RleEncodedStreamMetadata(\n        streamMetadata.physicalStreamType(),\n        streamMetadata.logicalStreamType(),\n        streamMetadata.logicalLevelTechnique1(),\n        streamMetadata.logicalLevelTechnique2(),\n        streamMetadata.physicalLevelTechnique(),\n        streamMetadata.numValues(),\n        streamMetadata.byteLength(),\n        rleInfo[0],\n        rleInfo[1]);\n  }\n\n  public int runs() {\n    return this.runs;\n  }\n\n  public int numRleValues() {\n    return this.numRleValues;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/StreamMetadata.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport me.lemire.integercompression.IntWrapper;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.decoder.DecodingUtils;\n\npublic class StreamMetadata {\n  private final PhysicalStreamType physicalStreamType;\n  private final LogicalStreamType logicalStreamType;\n  private final LogicalLevelTechnique logicalLevelTechnique1;\n  private final LogicalLevelTechnique logicalLevelTechnique2;\n  private final PhysicalLevelTechnique physicalLevelTechnique;\n  /* After logical Level technique was applied -> when rle is used it is the length of the runs and values array */\n  private final int numValues;\n  private final int byteLength;\n\n  // TODO: refactor -> use builder pattern\n  public StreamMetadata(\n      PhysicalStreamType physicalStreamType,\n      LogicalStreamType logicalStreamType,\n      LogicalLevelTechnique logicalLevelTechnique1,\n      LogicalLevelTechnique logicalLevelTechnique2,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      int numValues,\n      int byteLength) {\n    this.physicalStreamType = physicalStreamType;\n    this.logicalStreamType = logicalStreamType;\n    this.logicalLevelTechnique1 = logicalLevelTechnique1;\n    this.logicalLevelTechnique2 = logicalLevelTechnique2;\n    this.physicalLevelTechnique = physicalLevelTechnique;\n    this.numValues = numValues;\n    this.byteLength = byteLength;\n  }\n\n  private int getLogicalType() {\n    if (logicalStreamType == null) {\n      return 0;\n    }\n\n    if (logicalStreamType.dictionaryType() != null) {\n      return logicalStreamType.dictionaryType().ordinal();\n    }\n\n    if (logicalStreamType.lengthType() != null) {\n      return logicalStreamType.lengthType().ordinal();\n    }\n\n    return logicalStreamType.offsetType().ordinal();\n  }\n\n  public ArrayList<byte[]> encode() throws IOException {\n    return encode(5);\n  }\n\n  public ArrayList<byte[]> encode(int estimatedAdditionalStreams) throws IOException {\n    final var encodedStreamType = (byte) ((physicalStreamType.ordinal()) << 4 | getLogicalType());\n    final var encodedEncodingScheme =\n        (byte)\n            (logicalLevelTechnique1.ordinal() << 5\n                | logicalLevelTechnique2.ordinal() << 2\n                | physicalLevelTechnique.ordinal());\n\n    final var size =\n        2 + EncodingUtils.getVarIntSize(numValues) + EncodingUtils.getVarIntSize(byteLength);\n    final var varintBuffer = ByteBuffer.wrap(new byte[size]);\n    varintBuffer.put(encodedStreamType);\n    varintBuffer.put(encodedEncodingScheme);\n    EncodingUtils.putVarInt(numValues, varintBuffer);\n    EncodingUtils.putVarInt(byteLength, varintBuffer);\n\n    final var result = new ArrayList<byte[]>(1 + estimatedAdditionalStreams);\n    result.add(varintBuffer.array());\n    return result;\n  }\n\n  public static StreamMetadata decode(byte[] tile, IntWrapper offset) throws IOException {\n    var streamType = tile[offset.get()];\n    var physicalStreamType = PhysicalStreamType.values()[streamType >> 4];\n    LogicalStreamType logicalStreamType =\n        switch (physicalStreamType) {\n          case DATA -> new LogicalStreamType(DictionaryType.values()[streamType & 0xf]);\n          case OFFSET -> new LogicalStreamType(OffsetType.values()[streamType & 0xf]);\n          case LENGTH -> new LogicalStreamType(LengthType.values()[streamType & 0xf]);\n          default -> null;\n        };\n    offset.increment();\n\n    var encodingsHeader = tile[offset.get()] & 0xFF;\n    var logicalLevelTechnique1 = LogicalLevelTechnique.values()[encodingsHeader >> 5];\n    var logicalLevelTechnique2 = LogicalLevelTechnique.values()[encodingsHeader >> 2 & 0x7];\n    var physicalLevelTechnique = PhysicalLevelTechnique.values()[encodingsHeader & 0x3];\n    offset.increment();\n    var sizeInfo = DecodingUtils.decodeVarints(tile, offset, 2);\n    var numValues = sizeInfo[0];\n    var byteLength = sizeInfo[1];\n\n    return new StreamMetadata(\n        physicalStreamType,\n        logicalStreamType,\n        logicalLevelTechnique1,\n        logicalLevelTechnique2,\n        physicalLevelTechnique,\n        numValues,\n        byteLength);\n  }\n\n  public PhysicalStreamType physicalStreamType() {\n    return this.physicalStreamType;\n  }\n\n  public LogicalStreamType logicalStreamType() {\n    return this.logicalStreamType;\n  }\n\n  public LogicalLevelTechnique logicalLevelTechnique1() {\n    return this.logicalLevelTechnique1;\n  }\n\n  public LogicalLevelTechnique logicalLevelTechnique2() {\n    return this.logicalLevelTechnique2;\n  }\n\n  public PhysicalLevelTechnique physicalLevelTechnique() {\n    return this.physicalLevelTechnique;\n  }\n\n  public int numValues() {\n    return this.numValues;\n  }\n\n  public int byteLength() {\n    return this.byteLength;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/stream/StreamMetadataDecoder.java",
    "content": "package org.maplibre.mlt.metadata.stream;\n\nimport java.io.IOException;\nimport me.lemire.integercompression.IntWrapper;\n\npublic class StreamMetadataDecoder {\n\n  public static StreamMetadata decode(byte[] tile, IntWrapper offset) throws IOException {\n    var streamMetadata = StreamMetadata.decode(tile, offset);\n    /* Currently morton can't be combined with RLE only with delta */\n    if (streamMetadata.logicalLevelTechnique1().equals(LogicalLevelTechnique.MORTON)) {\n      return MortonEncodedStreamMetadata.decodePartial(streamMetadata, tile, offset);\n    }\n    /* Boolean rle doesn't need additional information */\n    else if ((LogicalLevelTechnique.RLE.equals(streamMetadata.logicalLevelTechnique1())\n            || LogicalLevelTechnique.RLE.equals(streamMetadata.logicalLevelTechnique2()))\n        && !PhysicalLevelTechnique.NONE.equals(streamMetadata.physicalLevelTechnique())) {\n      return RleEncodedStreamMetadata.decodePartial(streamMetadata, tile, offset);\n    }\n\n    return streamMetadata;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/metadata/tileset/MltMetadata.java",
    "content": "package org.maplibre.mlt.metadata.tileset;\n\nimport jakarta.annotation.Nullable;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.SequencedCollection;\nimport org.jetbrains.annotations.NotNull;\n\npublic final class MltMetadata {\n  private MltMetadata() {}\n\n  public static FieldType scalarFieldType(@NotNull ScalarType type, boolean isNullable) {\n    return new FieldType(new ScalarField(type), isNullable);\n  }\n\n  public static FieldType idFieldType(boolean hasLongId, boolean isNullable) {\n    return new FieldType(new ScalarField(LogicalScalarType.ID, hasLongId), isNullable);\n  }\n\n  public static FieldType structFieldType(@Nullable SequencedCollection<Field> children) {\n    return new FieldType(new ComplexField(ComplexType.STRUCT, children), false);\n  }\n\n  public static FieldType geometryFieldType() {\n    return new FieldType(new ComplexField(ComplexType.GEOMETRY), false);\n  }\n\n  public static FieldType complexFieldType(@NotNull ComplexType type, boolean isNullable) {\n    return new FieldType(new ComplexField(type, null), isNullable);\n  }\n\n  /// Describes how the column's values are associated with features and geometries\n  public enum ColumnScope {\n    /** 1:1 Mapping of property and feature to id and geometry */\n    FEATURE,\n    /** For M-Values, 1:1 Mapping for property and vertex */\n    VERTEX,\n    UNRECOGNIZED\n  }\n\n  public enum ScalarType {\n    BOOLEAN,\n    INT_8,\n    UINT_8,\n    INT_32,\n    UINT_32,\n    INT_64,\n    UINT_64,\n    FLOAT,\n    DOUBLE,\n    STRING,\n    UNRECOGNIZED\n  }\n\n  public enum ComplexType {\n    GEOMETRY,\n    STRUCT,\n    MAP, // nested property map\n    UNRECOGNIZED\n  }\n\n  public enum LogicalScalarType {\n    ID,\n    UNRECOGNIZED\n  }\n\n  public enum LogicalComplexType {\n    /** physical type: list&lt;UInt8&gt; */\n    BINARY,\n    /**\n     * physical type: map&lt;vec2&lt;double, T&gt;&gt; -&gt; special data structure which can be\n     * used for an efficient representation of linear referencing\n     */\n    RANGE_MAP,\n    UNRECOGNIZED\n  }\n\n  public static final class TileSetMetadata {\n    public List<FeatureTable> featureTables = new ArrayList<>();\n    public String name;\n    public String description;\n    public Object attribution;\n\n    @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n    public Optional<Integer> minZoom = Optional.empty();\n\n    @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n    public Optional<Integer> maxZoom = Optional.empty();\n\n    public List<Double> bounds = new ArrayList<>();\n    public List<Double> center = new ArrayList<>();\n  }\n\n  public static final record FeatureTable(\n      @NotNull String name, @NotNull SequencedCollection<Column> columns) {\n\n    public FeatureTable {\n      Objects.requireNonNull(name);\n      columns = (columns != null) ? columns : new ArrayList<>();\n    }\n\n    public FeatureTable(String name) {\n      this(name, null);\n    }\n\n    public FeatureTable(String name, int initialColumnCapacity) {\n      this(name, new ArrayList<>(initialColumnCapacity));\n    }\n  }\n\n  /// The type of data in a Field\n  /// @param scalarType A scalar type, if set.  Mutually exclusive with complexType.\n  /// @param complexType A complex type, if set.  Mutually exclusive with scalarType.\n  /// @param isNullable Whether the field can be null\n  public static final record FieldType(\n      @Nullable ScalarField scalarType, @Nullable ComplexField complexType, boolean isNullable) {\n\n    public FieldType {\n      if ((scalarType == null) == (complexType == null)) {\n        throw new IllegalStateException(\"FieldType must be either scalar or complex\");\n      }\n    }\n\n    /// Create a scalar type\n    /// @param scalarType the type\n    /// @param isNullable whether the field can be null\n    public FieldType(@Nullable ScalarField scalarType, boolean isNullable) {\n      this(scalarType, null, isNullable);\n    }\n\n    /// Create a complex type\n    /// @param complexType the type\n    /// @param isNullable whether the field can be null\n    public FieldType(@Nullable ComplexField complexType, boolean isNullable) {\n      this(null, complexType, isNullable);\n    }\n\n    /// Check if this field is of the given scalar type\n    /// @param type the scalar type to check against\n    /// @return true if this field is of the given scalar type\n    public boolean is(ScalarType type) {\n      return (scalarType != null && scalarType.physicalType == type);\n    }\n\n    /// Check if this field is of the given logical scalar type\n    /// @param type the logical scalar type to check against\n    /// @return true if this field is of the given logical scalar type\n    public boolean is(LogicalScalarType type) {\n      return (scalarType != null && scalarType.logicalType == type);\n    }\n\n    /// Check if this field is of the given complex type\n    /// @param type the complex type to check against\n    /// @return true if this field is of the given complex type\n    public boolean is(ComplexType type) {\n      return (complexType != null && complexType.physicalType == type);\n    }\n\n    /// Check if this field is of the given logical complex type\n    /// @param type the logical complex type to check against\n    /// @return true if this field is of the given logical complex type\n    public boolean is(LogicalComplexType type) {\n      return (complexType != null && complexType.logicalType == type);\n    }\n\n    /// Get the physical scalar type of this field, if applicable\n    /// @return an Optional containing the physical scalar type of this field, or an empty Optional\n    /// if this field is not a scalar type\n    public Optional<ScalarType> getScalarType() {\n      return (scalarType != null) ? Optional.ofNullable(scalarType.physicalType) : Optional.empty();\n    }\n\n    /// Get the logical scalar type of this field, if applicable\n    /// @return an Optional containing the logical scalar type of this field, or an empty Optional\n    /// if this field is not a scalar type\n    public Optional<LogicalScalarType> getLogicalScalarType() {\n      return (scalarType != null) ? Optional.ofNullable(scalarType.logicalType) : Optional.empty();\n    }\n\n    /// Get the physical complex type of this field, if applicable\n    /// @return an Optional containing the physical complex type of this field, or an empty Optional\n    /// if this field is not a complex type\n    public Optional<ComplexType> getComplexType() {\n      return (complexType != null)\n          ? Optional.ofNullable(complexType.physicalType)\n          : Optional.empty();\n    }\n\n    /// Get the logical complex type of this field, if applicable\n    /// @return an Optional containing the logical complex type of this field, or an empty Optional\n    /// if this field is not a complex type\n    public Optional<LogicalComplexType> getLogicalComplexType() {\n      return (complexType != null)\n          ? Optional.ofNullable(complexType.logicalType)\n          : Optional.empty();\n    }\n\n    /// Get the child fields of this field, if applicable (e.g., for STRUCT or MAP types)\n    /// @return an Optional containing a SequencedCollection of child fields, or an empty Optional\n    public Optional<SequencedCollection<Field>> getChildren() {\n      return (complexType != null) ? Optional.ofNullable(complexType.children) : Optional.empty();\n    }\n  }\n\n  /// A field may be a column or nested as a child of a complex type\n  /// @param name The name of the field.  May be null for nested fields or implicit types (e.g, ID).\n  /// @param type The type of the field.  Must be non-null.\n  public static final record Field(@NotNull FieldType type, @Nullable String name) {\n    public Field {\n      Objects.requireNonNull(type);\n    }\n\n    public Field(@NotNull FieldType type) {\n      this(type, null);\n    }\n  }\n\n  /// Column are top-level types in the schema\n  /// @param field The field associated with this column.  Must be non-null.\n  /// @param columnScope The column scope, which must be either FEATURE or VERTEX\n  public static final record Column(@NotNull Field field, @NotNull ColumnScope columnScope) {\n\n    /// Create a column with the given field and column scope\n    /// @param field the field associated with this column\n    /// @param columnScope the column scope, which must be either FEATURE or VERTEX\n    public Column {\n      if (columnScope != ColumnScope.FEATURE && columnScope != ColumnScope.VERTEX) {\n        throw new IllegalStateException(\"Column scope must be either FEATURE or VERTEX\");\n      }\n    }\n\n    /// Create a column with the given field and default scope of FEATURE\n    /// @param field the field associated with this column\n    public Column(Field field) {\n      this(field, ColumnScope.FEATURE);\n    }\n\n    /// Create a column with the given type and default scope of FEATURE\n    /// @param type the type of the field associated with this column\n    public Column(FieldType type) {\n      this(new Field(type), ColumnScope.FEATURE);\n    }\n\n    /// Get the name of this column\n    /// @return the name of this column, or null if unnamed\n    public String getName() {\n      return field.name;\n    }\n\n    /// Check if this column is nullable\n    /// @return true if this column is nullable, false otherwise\n    public boolean isNullable() {\n      return field.type.isNullable;\n    }\n\n    /// Check if this column is a scalar type\n    /// @return true if this column is a scalar type, false otherwise\n    public boolean isScalar() {\n      return field.type.scalarType != null;\n    }\n\n    /// Check if this column is a complex type\n    /// @return true if this column is a complex type, false otherwise\n    public boolean isComplex() {\n      return field.type.complexType != null;\n    }\n\n    /// Check if this column is of the given scalar type\n    /// @param type the scalar type to check against\n    /// @return true if this column is of the given scalar type\n    public boolean is(ScalarType type) {\n      return field.type.is(type);\n    }\n\n    /// Check if this column is of the given logical scalar type\n    /// @param type the logical scalar type to check against\n    /// @return true if this column is of the given logical scalar type\n    public boolean is(LogicalScalarType type) {\n      return field.type.is(type);\n    }\n\n    /// Check if this column is of the given complex type\n    /// @param type the complex type to check against\n    /// @return true if this column is of the given complex type\n    public boolean is(ComplexType type) {\n      return field.type.is(type);\n    }\n\n    /// Check if this column is of the given logical complex type\n    /// @param type the logical complex type to check against\n    /// @return true if this column is of the given logical complex type\n    public boolean is(LogicalComplexType type) {\n      return field.type.is(type);\n    }\n\n    /// Get the physical scalar type of this column, if applicable\n    /// @return an Optional containing the physical scalar type of this column, or an empty Optional\n    // if this column is not a scalar type\n    public Optional<ScalarType> getScalarType() {\n      return field.type.getScalarType();\n    }\n\n    /// Get the logical scalar type of this column, if applicable\n    /// @return an Optional containing the logical scalar type of this column, or an empty Optional\n    // if this column is not a scalar type\n    public Optional<LogicalScalarType> getLogicalScalarType() {\n      return field.type.getLogicalScalarType();\n    }\n\n    /// Get the physical complex type of this column, if applicable\n    /// @return an Optional containing the physical complex type of this column, or an empty\n    // Optional if this column is not a complex type\n    public Optional<ComplexType> getComplexType() {\n      return field.type.getComplexType();\n    }\n\n    /// Get the logical complex type of this column, if applicable\n    /// @return an Optional containing the logical complex type of this column, or an empty Optional\n    // if this column is not a complex type\n    public Optional<LogicalComplexType> getLogicalComplexType() {\n      return field.type.getLogicalComplexType();\n    }\n\n    /// Get the child fields of this column, if applicable (e.g., for STRUCT or MAP types)\n    /// @return an Optional containing a SequencedCollection of child fields, or an empty Optional\n    public Optional<SequencedCollection<Field>> getChildren() {\n      return field.type.getChildren();\n    }\n  }\n\n  /// A scalar field, which may be a physical type or a simple logical type (e.g., ID)\n  /// @param physicalType The physical type, if applicable.  Mutually exclusive with logicalType\n  /// @param logicalType The logical type, if applicable.  Mutually exclusive with physicalType\n  /// @param hasLongId Whether the ID field has long ids (i.e., 64-bit integers)\n  public static final record ScalarField(\n      @Nullable ScalarType physicalType,\n      @Nullable LogicalScalarType logicalType,\n      boolean hasLongId) {\n\n    public ScalarField {\n      if ((physicalType == null) == (logicalType == null)) {\n        throw new IllegalStateException(\n            \"ScalarField must have either a physical type or a logical type\");\n      }\n    }\n\n    /// Create a scalar field with the given physical type\n    /// @param type the physical type of the field\n    public ScalarField(@NotNull ScalarType type) {\n      this(type, null, false);\n    }\n\n    /// Create a scalar field with the given logical type\n    /// @param type the logical type of the field\n    /// @param hasLongId whether the ID field has long ids (i.e., 64-bit integers)\n    public ScalarField(@NotNull LogicalScalarType type, boolean hasLongId) {\n      this(null, type, hasLongId);\n    }\n  }\n\n  /// A complex field\n  /// @param physicalType The physical type, if applicable.  Mutually exclusive with logicalType\n  /// @param logicalType The logical type, if applicable.  Mutually exclusive with physicalType.\n  /// @param children The child fields of this complex field, if applicable\n  public static final record ComplexField(\n      @Nullable ComplexType physicalType,\n      @Nullable LogicalComplexType logicalType,\n      @NotNull SequencedCollection<Field> children) {\n\n    public ComplexField {\n      if ((physicalType == null) == (logicalType == null)) {\n        throw new IllegalStateException(\n            \"ComplexField must have either a physical type or a logical type\");\n      }\n      children = (children != null) ? children : new ArrayList<>();\n    }\n\n    /// Create a complex field with the given physical type and no children\n    /// @param type the physical type of the field\n    public ComplexField(@NotNull ComplexType type) {\n      this(type, null);\n    }\n\n    /// Create a complex field with the given physical type and children\n    /// @param type the physical type of the field\n    /// @param children the child fields of this complex field, or null if no children\n    public ComplexField(@NotNull ComplexType type, @Nullable SequencedCollection<Field> children) {\n      this(type, null, children);\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/util/ByteArrayUtil.java",
    "content": "package org.maplibre.mlt.util;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Collection;\n\npublic final class ByteArrayUtil {\n  private ByteArrayUtil() {}\n\n  public static int totalLength(Collection<byte[]> buffers) {\n    return buffers.stream().mapToInt(x -> x.length).sum();\n  }\n\n  public static <T extends OutputStream> T concat(T stream, Collection<byte[]> buffers)\n      throws IOException {\n    for (var buffer : buffers) {\n      stream.write(buffer);\n    }\n    return stream;\n  }\n\n  public static byte[] concat(Collection<byte[]> buffers) throws IOException {\n    try (final var stream = new ByteArrayOutputStream(totalLength(buffers))) {\n      return concat(stream, buffers).toByteArray();\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/util/ExceptionUtil.java",
    "content": "package org.maplibre.mlt.util;\n\nimport java.util.function.Function;\nimport org.apache.commons.lang3.exception.UncheckedException;\n\npublic class ExceptionUtil {\n  @FunctionalInterface\n  public interface ThrowingFunction<T, R, E extends Exception> {\n    R apply(T t) throws E;\n  }\n\n  /// Wraps a function that throws a checked exception in a RuntimeException, allowing\n  /// it to be used in contexts that don't allow checked exceptions, e.g., streams.\n  public static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {\n    return t -> {\n      try {\n        return f.apply(t);\n      } catch (RuntimeException e) {\n        throw e;\n      } catch (Exception e) {\n        throw new UncheckedException(e);\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/util/OptionalUtil.java",
    "content": "package org.maplibre.mlt.util;\n\nimport java.util.Comparator;\nimport java.util.Optional;\nimport java.util.function.BiFunction;\nimport org.jetbrains.annotations.NotNull;\n\npublic class OptionalUtil {\n  /// Compare two Optional values with the specified comparator.\n  // Empty is not less than any value, and any value is less than empty.\n  @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n  public static <T extends Comparable<T>> boolean isLessThan(\n      @NotNull Optional<T> a, @NotNull Optional<T> b) {\n    return isLessThan(a, b, Comparator.<T>naturalOrder());\n  }\n\n  /// Compare two Optional values.\n  // Empty is not less than any value, and any value is less than empty.\n  @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n  private static <T> boolean isLessThan(\n      @NotNull Optional<T> a, @NotNull Optional<T> b, @NotNull Comparator<T> comparator) {\n    if (a.isEmpty()) {\n      return false;\n    }\n    if (b.isEmpty()) {\n      return true;\n    }\n    return comparator.compare(a.get(), b.get()) < 0;\n  }\n\n  /// Map two Optional values to a single Optional value using the specified function.\n  /// The result is empty if either input is empty.\n  @SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n  public static <T, U, V> Optional<V> map(\n      Optional<T> a, Optional<U> b, BiFunction<? super T, ? super U, ? extends V> f) {\n    return a.flatMap(av -> b.map(bv -> f.apply(av, bv)));\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/org/maplibre/mlt/util/StreamUtil.java",
    "content": "package org.maplibre.mlt.util;\n\nimport java.util.Iterator;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Spliterator;\nimport java.util.Spliterators;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\nimport org.apache.commons.lang3.tuple.Pair;\n\npublic final class StreamUtil {\n  private StreamUtil() {}\n\n  /// Combine two streams into one by applying a function to pairs of elements from the input\n  /// streams.\n  /// The resulting stream will have the same number of elements as the shorter input stream, and\n  /// any remaining elements in the longer stream will be ignored.\n  /// The resulting stream:\n  /// - Is lazy and will only compute elements as needed, allowing for efficient processing of large\n  /// or infinite streams.\n  /// - Is parallel if either of the input streams is parallel.\n  /// - Has the intersection of the characteristics of input streams with DISTINCT and SORTED\n  /// removed\n  /// - Is sized if both input streams are sized, the size being the minimum of the sizes of the\n  /// input streams\n  /// @param a the first input stream, providing the first argument to the function\n  /// @param b the second input stream, providing the second argument to the function\n  /// @param f the function to apply to pairs of elements from the input streams\n  /// @return a stream of the results of applying the function to pairs of elements from the input\n  /// streams\n  /// @throws NullPointerException if any of the input streams or the function is null\n  // Really, this isn't in `Stream` anywhere?\n  public static <A, B, C> Stream<C> zip(\n      Stream<? extends A> a,\n      Stream<? extends B> b,\n      BiFunction<? super A, ? super B, ? extends C> f) {\n    Objects.requireNonNull(f);\n    final var spliterA = Objects.requireNonNull(a).spliterator();\n    final var spliterB = Objects.requireNonNull(b).spliterator();\n    final var cIterator = new ZipIterator<A, B, C>(spliterA, spliterB, f);\n\n    // Eliminate DISTINCT and SORTED characteristics\n    final int characteristics =\n        spliterA.characteristics()\n            & spliterB.characteristics()\n            & ~(Spliterator.DISTINCT | Spliterator.SORTED);\n\n    // the zipped result will be the size of the smaller stream\n    final var sizeIfKnown =\n        ((characteristics & Spliterator.SIZED) != 0)\n            ? Math.min(spliterA.getExactSizeIfKnown(), spliterB.getExactSizeIfKnown())\n            : -1;\n\n    return StreamSupport.stream(\n        (sizeIfKnown < 0)\n            ? Spliterators.spliteratorUnknownSize(cIterator, characteristics)\n            : Spliterators.spliterator(cIterator, sizeIfKnown, characteristics),\n        a.isParallel() || b.isParallel());\n  }\n\n  private static final class ZipIterator<A, B, C> implements Iterator<C> {\n    private final Iterator<A> aIterator;\n    private final Iterator<B> bIterator;\n    private final BiFunction<? super A, ? super B, ? extends C> function;\n\n    ZipIterator(\n        Spliterator<? extends A> a,\n        Spliterator<? extends B> b,\n        BiFunction<? super A, ? super B, ? extends C> f) {\n      aIterator = Spliterators.iterator(a);\n      bIterator = Spliterators.iterator(b);\n      function = f;\n    }\n\n    @Override\n    public boolean hasNext() {\n      // Stop when either stream is exhausted, ignoring remaining elements in the longer stream\n      return aIterator.hasNext() && bIterator.hasNext();\n    }\n\n    @Override\n    public C next() {\n      return function.apply(aIterator.next(), bIterator.next());\n    }\n  }\n\n  /// Return a lazy stream of `Pair<>` objects\n  public static <A, B> Stream<Pair<A, B>> zip(Stream<? extends A> a, Stream<? extends B> b) {\n    return zip(a, b, Pair::of);\n  }\n\n  /// Run the given function for each pair of elements from the two streams.\n  /// @return the number of pairs processed\n  public static <A, B> long zipEach(\n      Stream<? extends A> a, Stream<? extends B> b, BiConsumer<? super A, ? super B> f) {\n    return zip(\n            a,\n            b,\n            (x, y) -> {\n              f.accept(x, y);\n              return 1L;\n            })\n        .reduce(0L, Long::sum);\n  }\n\n  /// Filter a stream of objects by those that are of the given type or a subtype.\n  /// Intended for use with `Stream.flatMap` to safely change the type within a sequence of chained\n  /// operations.\n  /// @param targetType the type to filter by\n  /// @return A stream of the target type\n  public static <Target extends Base, Base> Function<Base, Stream<Target>> ofType(\n      Class<Target> targetType) {\n    return value ->\n        targetType.isInstance(value) ? Stream.of(targetType.cast(value)) : Stream.empty();\n  }\n\n  /// The equivalent of #ofType for stream-like operations on optional values.\n  /// Intended for use with `Optional.flatMap` to safelyt change the type within a sequence of\n  /// chained operations.\n  /// @param targetType the type\n  /// @return An optional of the target type if the value is of that type, otherwise empty\n  public static <Target extends Base, Base> Function<Base, Optional<Target>> optionalOfType(\n      Class<Target> targetType) {\n    return value ->\n        targetType.isInstance(value) ? Optional.of(targetType.cast(value)) : Optional.empty();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/springmeyer/Pbf.java",
    "content": "package org.springmeyer;\n\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\n/*\n * This is a partial port of https://github.com/mapbox/pbf to Java.\n */\npublic class Pbf {\n  private byte[] buf;\n  public int pos;\n  public int type;\n  public int length;\n\n  public static final int Varint = 0;\n  public static final int Fixed64 = 1;\n  public static final int Bytes = 2;\n  public static final int Fixed32 = 5;\n\n  private static final long SHIFT_LEFT_32 = (1L << 16) * (1L << 16);\n\n  public Pbf(byte[] buf) {\n    this.buf = buf != null ? buf : new byte[0];\n    this.pos = 0;\n    this.type = 0;\n    this.length = this.buf.length;\n  }\n\n  public <T> T readFields(ReadField<T> readField, T result, int end) {\n    end = end == 0 ? this.length : end;\n\n    while (this.pos < end) {\n      int val = this.readVarint();\n      int tag = val >> 3;\n      int startPos = this.pos;\n\n      this.type = val & 0x7;\n      readField.read(tag, result, this);\n\n      if (this.pos == startPos) this.skip(val);\n    }\n    return result;\n  }\n\n  public float readFloat() {\n    float val = Float.intBitsToFloat(readInt32(this.buf, this.pos));\n    this.pos += 4;\n    return val;\n  }\n\n  public long readFixed64() {\n    long val =\n        readUInt32(this.buf, this.pos)\n            + ((long) readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32);\n    this.pos += 8;\n    return val;\n  }\n\n  public double readDouble() {\n    double val = Double.longBitsToDouble(readFixed64());\n    this.pos += 8;\n    return val;\n  }\n\n  public int readVarint() {\n    return this.readVarint(false);\n  }\n\n  public int readVarint(boolean isSigned) {\n    final var buf = this.buf;\n\n    int b = buf[this.pos++];\n    int val = b & 0x7f;\n    if ((b & 0x80) == 0) return val;\n    b = buf[this.pos++];\n    val |= (b & 0x7f) << 7;\n    if ((b & 0x80) == 0) return val;\n    b = buf[this.pos++];\n    val |= (b & 0x7f) << 14;\n    if ((b & 0x80) == 0) return val;\n    b = buf[this.pos++];\n    val |= (b & 0x7f) << 21;\n    if ((b & 0x80) == 0) return val;\n\n    b = buf[this.pos];\n    val |= (b & 0x0f) << 28;\n\n    return readVarintRemainder(val, isSigned);\n  }\n\n  private int readVarintRemainder(int val, boolean isSigned) {\n    int b = this.buf[this.pos++];\n    int high = (b & 0x70) >> 4;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n    b = this.buf[this.pos++];\n    high |= (b & 0x7f) << 3;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n    b = this.buf[this.pos++];\n    high |= (b & 0x7f) << 10;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n    b = this.buf[this.pos++];\n    high |= (b & 0x7f) << 17;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n    b = this.buf[this.pos++];\n    high |= (b & 0x7f) << 24;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n    b = this.buf[this.pos++];\n    high |= (b & 0x01) << 31;\n    if ((b & 0x80) == 0) return toNum(val, high, isSigned);\n\n    throw new IllegalArgumentException(\"Expected varint not more than 10 bytes\");\n  }\n\n  private static int toNum(int low, int high, boolean isSigned) {\n    if (isSigned) {\n      return (int) (high * 0x100000000L) + (low >>> 0);\n    }\n    return (int) ((high >>> 0) * 0x100000000L) + (low >>> 0);\n  }\n\n  public long readVarint64() {\n    return this.readVarint(true);\n  }\n\n  public int readSVarint() {\n    int num = this.readVarint();\n    return num % 2 == 1 ? (num + 1) / -2 : num / 2;\n  }\n\n  public boolean readBoolean() {\n    return this.readVarint() != 0;\n  }\n\n  public String readString() {\n    int end = this.readVarint() + this.pos;\n    int pos = this.pos;\n    this.pos = end;\n    return new String(Arrays.copyOfRange(this.buf, pos, end), StandardCharsets.UTF_8);\n  }\n\n  public byte[] readBytes() {\n    int end = this.readVarint() + this.pos;\n    byte[] buffer = new byte[end - this.pos];\n    System.arraycopy(this.buf, this.pos, buffer, 0, buffer.length);\n    this.pos = end;\n    return buffer;\n  }\n\n  public void skip(int val) {\n    int type = val & 0x7;\n    if (type == Varint) {\n      while (this.buf[this.pos++] > 0x7F) {}\n    } else if (type == Bytes) {\n      this.pos = this.readVarint() + this.pos;\n    } else if (type == Fixed32) {\n      this.pos += 4;\n    } else if (type == Fixed64) {\n      this.pos += 8;\n    } else {\n      throw new IllegalArgumentException(\"Unimplemented type: \" + type);\n    }\n  }\n\n  private static int readUInt32(byte[] buf, int pos) {\n    return (buf[pos] & 0xFF)\n        | ((buf[pos + 1] & 0xFF) << 8)\n        | ((buf[pos + 2] & 0xFF) << 16)\n        | ((buf[pos + 3] & 0xFF) << 24);\n  }\n\n  private static int readInt32(byte[] buf, int pos) {\n    return (buf[pos] & 0xFF)\n        | ((buf[pos + 1] & 0xFF) << 8)\n        | ((buf[pos + 2] & 0xFF) << 16)\n        | ((buf[pos + 3] & 0xFF) << 24);\n  }\n\n  @FunctionalInterface\n  public interface ReadField<T> {\n    void read(int tag, T result, Pbf pbf);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/springmeyer/Point.java",
    "content": "package org.springmeyer;\n\npublic class Point {\n  public int x;\n  public int y;\n\n  public Point(int x, int y) {\n    this.x = x;\n    this.y = y;\n  }\n\n  public Point clone() {\n    return new Point(this.x, this.y);\n  }\n\n  public String toString() {\n    return \"{x:\" + this.x + \", y:\" + this.y + \"}\";\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/springmeyer/VectorTile.java",
    "content": "package org.springmeyer;\n\nimport java.util.Map;\n\n/*\n * This is a java port of https://github.com/mapbox/vector-tile-js\n */\npublic class VectorTile {\n  public Map<String, VectorTileLayer> layers;\n\n  public VectorTile(Pbf pbf, int end) throws IllegalArgumentException {\n    this.layers = pbf.readFields(VectorTile::readTile, new java.util.LinkedHashMap<>(), end);\n  }\n\n  private static void readTile(int tag, Map<String, VectorTileLayer> layers, Pbf pbf)\n      throws IllegalArgumentException {\n    if (tag == 3) {\n      VectorTileLayer layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);\n      if (layer.length > 0) {\n        layers.put(layer.name, layer);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/springmeyer/VectorTileFeature.java",
    "content": "package org.springmeyer;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class VectorTileFeature {\n  public Map<String, Object> properties;\n  public int extent;\n  public int type;\n  public long id;\n\n  private Pbf pbf;\n  private int geometry;\n  private List<String> keys;\n  private List<Object> values;\n\n  public static final String[] types = {\"Unknown\", \"Point\", \"LineString\", \"Polygon\"};\n\n  @SuppressWarnings(\"this-escape\")\n  public VectorTileFeature(Pbf pbf, int end, int extent, List<String> keys, List<Object> values) {\n    this.properties = new HashMap<>();\n    this.extent = extent;\n    this.type = 0;\n\n    this.pbf = pbf;\n    this.geometry = -1;\n    this.keys = keys;\n    this.values = values;\n\n    pbf.readFields(this::readFeature, this, end);\n  }\n\n  private void readFeature(int tag, VectorTileFeature feature, Pbf pbf) {\n    if (tag == 1) {\n      feature.id = pbf.readVarint();\n    } else if (tag == 2) {\n      feature.readTag(pbf, feature);\n    } else if (tag == 3) {\n      feature.type = pbf.readVarint();\n    } else if (tag == 4) {\n      feature.geometry = pbf.pos;\n    }\n  }\n\n  private void readTag(Pbf pbf, VectorTileFeature feature) {\n    int end = pbf.readVarint() + pbf.pos;\n\n    while (pbf.pos < end) {\n      String key = feature.keys.get(pbf.readVarint());\n      Object value = feature.values.get(pbf.readVarint());\n      feature.properties.put(key, value);\n    }\n  }\n\n  public List<List<Point>> loadGeometry() {\n    Pbf pbf = this.pbf;\n    pbf.pos = this.geometry;\n\n    int end = pbf.readVarint() + pbf.pos;\n    int cmd = 1;\n    int length = 0;\n    int x = 0;\n    int y = 0;\n    List<List<Point>> lines = new ArrayList<>();\n    List<Point> line = null;\n\n    while (pbf.pos < end) {\n      if (length <= 0) {\n        int cmdLen = pbf.readVarint();\n        cmd = cmdLen & 0x7;\n        length = cmdLen >> 3;\n      }\n\n      length--;\n\n      if (cmd == 1 || cmd == 2) {\n        x += pbf.readSVarint();\n        y += pbf.readSVarint();\n\n        if (cmd == 1) { // moveTo\n          if (line != null) lines.add(line);\n          line = new ArrayList<>();\n        }\n\n        line.add(new Point(x, y));\n\n      } else if (cmd == 7) {\n        // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90\n        if (line != null) {\n          line.add(line.get(0).clone()); // closePolygon\n        }\n      } else {\n        throw new IllegalArgumentException(\"unknown command \" + cmd);\n      }\n    }\n\n    if (line != null) lines.add(line);\n\n    return lines;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/main/java/springmeyer/VectorTileLayer.java",
    "content": "package org.springmeyer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class VectorTileLayer {\n  // Public\n  public int version = 1;\n  public String name = null;\n  public int extent = 4096;\n  public int length = 0;\n\n  // Private\n  private final Pbf pbf;\n  private final List<String> keys = new ArrayList<>();\n  private final List<Object> values = new ArrayList<>();\n  private final List<Integer> features = new ArrayList<>();\n\n  @SuppressWarnings(\"this-escape\")\n  public VectorTileLayer(Pbf pbf, int end) throws IllegalArgumentException {\n    this.pbf = pbf;\n    pbf.readFields(VectorTileLayer::readLayer, this, end);\n    this.length = this.features.size();\n  }\n\n  private static void readLayer(int tag, VectorTileLayer layer, Pbf pbf)\n      throws IllegalArgumentException {\n    switch (tag) {\n      case 15:\n        layer.version = pbf.readVarint();\n        break;\n      case 1:\n        layer.name = pbf.readString();\n        break;\n      case 5:\n        layer.extent = pbf.readVarint();\n        break;\n      case 2:\n        layer.features.add(pbf.pos);\n        break;\n      case 3:\n        layer.keys.add(pbf.readString());\n        break;\n      case 4:\n        layer.values.add(readValueMessage(pbf));\n        break;\n      default:\n        break;\n    }\n  }\n\n  private static Object readValueMessage(Pbf pbf) throws IllegalArgumentException {\n    Object value = null;\n    int end = pbf.readVarint() + pbf.pos;\n    while (pbf.pos < end) {\n      int tag = pbf.readVarint() >> 3;\n      if (tag == 1) {\n        value = pbf.readString();\n      } else if (tag == 2) {\n        value = pbf.readFloat();\n      } else if (tag == 3) {\n        value = pbf.readDouble();\n      } else if (tag == 4) {\n        value = pbf.readVarint64();\n      } else if (tag == 5) {\n        value = pbf.readVarint();\n      } else if (tag == 6) {\n        value = pbf.readSVarint();\n      } else if (tag == 7) {\n        value = pbf.readBoolean();\n      }\n    }\n\n    return value;\n  }\n\n  public VectorTileFeature feature(int i) throws IllegalArgumentException {\n    if (i < 0 || i >= this.features.size()) {\n      throw new IndexOutOfBoundsException(\"feature index out of bounds\");\n    }\n\n    this.pbf.pos = this.features.get(i).intValue();\n\n    int end = this.pbf.readVarint() + this.pbf.pos;\n    return new VectorTileFeature(this.pbf, end, this.extent, this.keys, this.values);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/MltGenerator.java",
    "content": "package org.maplibre.mlt;\n\nimport static org.maplibre.mlt.TestSettings.ID_REASSIGNABLE_MVT_LAYERS;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\npublic class MltGenerator {\n  private static final int MIN_ZOOM = 0;\n  private static final int MAX_ZOOM = 2;\n  private static final String TILESET_METADATA_FILE_NAME = \"tileset.pbf\";\n\n  private static final String MVT_SPECIFIC_TILES_SOURCE_MBTILES = \"\";\n  private static final String MVT_SPECIFIC_TILES_SOURCE_DIR =\n      \"..\\\\ts\\\\test\\\\data\\\\omt\\\\optimized\\\\mvt\";\n  private static final String MLT_SPECIFIC_TILES_OUTPUT_DIR =\n      \"..\\\\ts\\\\test\\\\data\\\\omt\\\\optimized\\\\mlt\\\\optimized\";\n  private static final String MVT_SPECIFIC_TILES_OUTPUT_DIR =\n      \"..\\\\ts\\\\test\\\\data\\\\omt\\\\optimized\\\\mvt\";\n\n  private static final String MBTILES_FILE = \"\";\n  private static final String MVT_OUTPUT_DIR = \"..\\\\test\\\\data\\\\optimized\\\\omt\\\\mvt\";\n  private static final String MLT_OUTPUT_DIR = \"..\\\\test\\\\data\\\\optimized\\\\omt\\\\mlt\\\\plain\";\n\n  private static final TestUtils.Optimization DEFAULT_OPTIMIZATION = TestUtils.Optimization.NONE;\n  protected static final ColumnMappingConfig COLUMN_MAPPINGS =\n      ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(new ColumnMapping(\"name\", \"_\", true)));\n  private static final boolean DEFAULT_USE_FAST_PFOR = false;\n  private static final boolean DEFAULT_USE_FSST = false;\n  private static final boolean DEFAULT_USE_POLYGON_TESSELLATION = false;\n  private static final boolean DEFAULT_USE_MORTON_ENCODING = false;\n  private static final boolean DEFAULT_INCLUDE_IDS = true;\n  private static final ConversionConfig.TypeMismatchPolicy DEFAULT_MISMATCH_POLICY =\n      ConversionConfig.TypeMismatchPolicy.FAIL;\n  private static final List<String> OUTLINE_POLYGON_FEATURE_TABLE_NAMES = List.of(\"building\");\n\n  @Test\n  @Disabled\n  public void generateMltTileset() throws IOException, SQLException, ClassNotFoundException {\n    var mbTilesFilename = \"jdbc:sqlite:\" + MBTILES_FILE;\n    try (var repo = new MbtilesRepository(mbTilesFilename, MIN_ZOOM, MAX_ZOOM)) {\n      final var optimizations = getOptimizations();\n\n      for (var mvTile : repo) {\n        final var tileId = mvTile.tileId();\n        try {\n          final var isIdPresent = false;\n          final var tileMetadata =\n              MltConverter.createTilesetMetadata(mvTile, COLUMN_MAPPINGS, isIdPresent);\n          final var mlTile = convertMvtToMlt(optimizations, true, mvTile, tileMetadata);\n\n          final var z = tileId.getLeft();\n          final var x = tileId.getMiddle();\n          final var y = (int) Math.pow(2, z) - tileId.getRight() - 1;\n          writeTile(mlTile, MLT_OUTPUT_DIR, \".mlt\", x, y, z);\n          final var rawMvTile = repo.getRawTile(tileId);\n          writeTile(rawMvTile, MVT_OUTPUT_DIR, \".mvt\", x, y, z);\n        } catch (Exception e) {\n          System.out.println(\"Error while processing tile \" + tileId);\n          e.printStackTrace();\n        }\n      }\n    }\n  }\n\n  @Test\n  @Disabled\n  public void generateSpecificMlTiles() throws IOException {\n    var mvtFileNames =\n        Files.list(Paths.get(MVT_SPECIFIC_TILES_SOURCE_DIR))\n            .filter(file -> !Files.isDirectory(file))\n            .map(Path::getFileName)\n            .toList();\n\n    var fullMvtFileNames =\n        mvtFileNames.stream()\n            .map(f -> Path.of(MVT_SPECIFIC_TILES_SOURCE_DIR, f.toString()))\n            .toList();\n    var mvTiles =\n        fullMvtFileNames.stream()\n            .map(\n                f -> {\n                  try {\n                    return MvtUtils.decodeMvt(Files.readAllBytes(f));\n                  } catch (IOException e) {\n                    throw new RuntimeException(e);\n                  }\n                })\n            .toList();\n\n    var optimizations = getOptimizations();\n\n    for (var tileName : mvtFileNames) {\n      var mvt = Files.readAllBytes(Path.of(MVT_SPECIFIC_TILES_SOURCE_DIR, tileName.toString()));\n      var mvTile = MvtUtils.decodeMvt(mvt);\n      try {\n        final var isIdPresent = false;\n        final var tileMetadata =\n            MltConverter.createTilesetMetadata(mvTile, COLUMN_MAPPINGS, isIdPresent);\n\n        var mlTile =\n            convertMvtToMlt(optimizations, DEFAULT_USE_POLYGON_TESSELLATION, mvTile, tileMetadata);\n\n        var mltFilename = tileName.toString().replace(\".mvt\", \".mlt\");\n        Files.write(Path.of(MLT_SPECIFIC_TILES_OUTPUT_DIR, mltFilename), mlTile);\n      } catch (Exception e) {\n        System.out.println(\"Error while processing tile \" + tileName);\n        e.printStackTrace();\n      }\n    }\n  }\n\n  @Test\n  @Disabled\n  public void generateSpecificMlTilesFromMbtiles()\n      throws IOException, SQLException, ClassNotFoundException {\n    try (var repo =\n        new MbtilesRepository(\"jdbc:sqlite:\" + MVT_SPECIFIC_TILES_SOURCE_MBTILES, 0, 14)) {\n      var mvTiles = repo.getLargestTilesPerZoom();\n      var optimizations = getOptimizations();\n      for (var mvTile : mvTiles) {\n        try {\n          final var isIdPresent = false;\n          final var tileMetadata =\n              MltConverter.createTilesetMetadata(mvTile.getMiddle(), COLUMN_MAPPINGS, isIdPresent);\n\n          var mlTile =\n              convertMvtToMlt(\n                  optimizations,\n                  DEFAULT_USE_POLYGON_TESSELLATION,\n                  mvTile.getMiddle(),\n                  tileMetadata);\n\n          var tileId = mvTile.getRight();\n          var tileName = tileId.getLeft() + \"_\" + tileId.getMiddle() + \"_\" + tileId.getRight();\n          Files.write(Path.of(MLT_SPECIFIC_TILES_OUTPUT_DIR, tileName + \".mlt\"), mlTile);\n          Files.write(Path.of(MVT_SPECIFIC_TILES_OUTPUT_DIR, tileName + \".mvt\"), mvTile.getLeft());\n        } catch (Exception e) {\n          System.out.println(\"Error while processing tile \" + mvTile);\n          e.printStackTrace();\n        }\n      }\n    }\n  }\n\n  private Map<String, FeatureTableOptimizations> getOptimizations() {\n    var allowSorting = DEFAULT_OPTIMIZATION == TestUtils.Optimization.SORTED;\n    // TODO: account for per-layer mappings\n    final var mappings =\n        COLUMN_MAPPINGS.entrySet().stream().flatMap(entry -> entry.getValue().stream()).toList();\n    var featureTableOptimization = new FeatureTableOptimizations(allowSorting, false, mappings);\n    var optimizations =\n        TestSettings.OPTIMIZED_MVT_LAYERS.stream()\n            .collect(Collectors.toMap(l -> l, l -> featureTableOptimization));\n\n    /* Only regenerate the ids for specific layers when the column is not sorted for comparison reasons */\n    if (DEFAULT_OPTIMIZATION == TestUtils.Optimization.IDS_REASSIGNED) {\n      for (var reassignableLayer : ID_REASSIGNABLE_MVT_LAYERS) {\n        optimizations.put(reassignableLayer, new FeatureTableOptimizations(false, true, mappings));\n      }\n    }\n    return optimizations;\n  }\n\n  private ConversionConfig.ConfigBuilder defaultConfigBuilder() {\n    return ConversionConfig.builder()\n        .includeIds(DEFAULT_INCLUDE_IDS)\n        .typeMismatchPolicy(DEFAULT_MISMATCH_POLICY)\n        .useFastPFOR(DEFAULT_USE_FAST_PFOR)\n        .useFSST(DEFAULT_USE_FSST)\n        .useMortonEncoding(DEFAULT_USE_MORTON_ENCODING)\n        .outlineFeatureTableNames(OUTLINE_POLYGON_FEATURE_TABLE_NAMES);\n  }\n\n  private byte[] convertMvtToMlt(\n      Map<String, FeatureTableOptimizations> optimizations,\n      boolean preTessellatePolygons,\n      MapboxVectorTile mvTile,\n      MltMetadata.TileSetMetadata tileMetadata)\n      throws IOException {\n    final var config =\n        defaultConfigBuilder()\n            .optimizations(optimizations)\n            .preTessellatePolygons(preTessellatePolygons)\n            .build();\n    return MltConverter.encode(mvTile, tileMetadata, config, null);\n  }\n\n  private static void writeTile(\n      byte[] tile, String outDir, String tileExtension, int x, int y, int z) throws IOException {\n    var path = Paths.get(outDir, Integer.toString(z), Integer.toString(x), Integer.toString(y));\n    path.toFile().mkdirs();\n    var mltFilename = path.resolve(y + tileExtension);\n    System.out.println(\"Writing tile to \" + mltFilename);\n    Files.write(mltFilename, tile);\n    var compressedTile = EncodingUtils.gzip(tile);\n    var compressedTileName = path.resolve(y + tileExtension + \".gz\");\n    Files.write(compressedTileName, compressedTile);\n  }\n}\n\nclass MbtilesRepository implements Iterable<MapboxVectorTile>, Closeable {\n  private static final String TILE_TABLE_NAME = \"tiles\";\n  private final Connection connection;\n  private final Statement statement;\n  protected final int minZoom;\n  protected final int maxZoom;\n\n  MbtilesRepository(String url, int minZoom, int maxZoom)\n      throws ClassNotFoundException, SQLException {\n    Class.forName(\"org.sqlite.JDBC\");\n    this.connection = DriverManager.getConnection(url);\n    this.statement = this.connection.createStatement();\n    this.minZoom = minZoom;\n    this.maxZoom = maxZoom;\n  }\n\n  protected MapboxVectorTile getTile(Triple<Integer, Integer, Integer> tileId) {\n    try {\n      var rs =\n          statement.executeQuery(\n              String.format(\n                  \"SELECT * FROM %s WHERE zoom_level = %d AND\"\n                      + \" tile_column = %d AND tile_row = %d;\",\n                  TILE_TABLE_NAME, tileId.getLeft(), tileId.getMiddle(), tileId.getRight()));\n\n      rs.next();\n      InputStream in = rs.getBinaryStream(\"tile_data\");\n      byte[] mvt = new byte[in.available()];\n      in.read(mvt);\n\n      var uncompressedMvt = EncodingUtils.unzip(mvt);\n      return MvtUtils.decodeMvt(uncompressedMvt);\n    } catch (SQLException | IOException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  protected byte[] getRawTile(Triple<Integer, Integer, Integer> tileId)\n      throws SQLException, IOException {\n    var rs =\n        statement.executeQuery(\n            String.format(\n                \"SELECT * FROM %s WHERE zoom_level = %d AND\"\n                    + \" tile_column = %d AND tile_row = %d;\",\n                TILE_TABLE_NAME, tileId.getLeft(), tileId.getMiddle(), tileId.getRight()));\n\n    rs.next();\n    InputStream in = rs.getBinaryStream(\"tile_data\");\n    byte[] mvt = new byte[in.available()];\n    in.read(mvt);\n\n    return EncodingUtils.unzip(mvt);\n  }\n\n  public List<Triple<byte[], MapboxVectorTile, Triple<Integer, Integer, Integer>>>\n      getLargestTilesPerZoom() {\n    try {\n      var mvTiles =\n          new ArrayList<Triple<byte[], MapboxVectorTile, Triple<Integer, Integer, Integer>>>();\n      for (var zoom = 0; zoom <= maxZoom; zoom++) {\n        var rs =\n            statement.executeQuery(\n                String.format(\n                    \"SELECT * FROM %s WHERE zoom_level = %d \"\n                        + \"ORDER BY LENGTH(tile_data) DESC LIMIT 1;\",\n                    TILE_TABLE_NAME, zoom));\n\n        rs.next();\n        InputStream in = rs.getBinaryStream(\"tile_data\");\n        byte[] mvt = new byte[in.available()];\n        in.read(mvt);\n        var x = rs.getInt(\"tile_column\");\n        var y = rs.getInt(\"tile_row\");\n\n        var uncompressedMvt = EncodingUtils.unzip(mvt);\n        var decodedMvt = MvtUtils.decodeMvt(uncompressedMvt);\n        var tileId = Triple.of(zoom, x, y);\n        mvTiles.add(Triple.of(uncompressedMvt, decodedMvt, tileId));\n      }\n\n      return mvTiles;\n    } catch (SQLException | IOException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  public Queue<Triple<Integer, Integer, Integer>> getTileIds() {\n    try {\n      // TODO: read in batches to scale also for a planet-scale tileset\n      var rs =\n          statement.executeQuery(\n              String.format(\n                  \"SELECT * FROM %s WHERE zoom_level >= %d AND zoom_level <= %d\",\n                  TILE_TABLE_NAME, minZoom, maxZoom));\n      var tileIds = new LinkedList<Triple<Integer, Integer, Integer>>();\n      while (rs.next()) {\n        int z = rs.getInt(\"zoom_level\");\n        int x = rs.getInt(\"tile_column\");\n        int y = rs.getInt(\"tile_row\");\n        var tileId = Triple.of(z, x, y);\n        tileIds.add(tileId);\n      }\n\n      return tileIds;\n    } catch (SQLException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  public void close() {\n    try {\n      statement.close();\n      connection.close();\n    } catch (SQLException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @NotNull\n  @Override\n  public Iterator<MapboxVectorTile> iterator() {\n    return new MbtilesIterator();\n  }\n\n  private class MbtilesIterator implements Iterator<MapboxVectorTile> {\n    private Queue<Triple<Integer, Integer, Integer>> tileIds;\n\n    @Override\n    public boolean hasNext() {\n      if (tileIds == null) {\n        tileIds = getTileIds();\n      }\n\n      return !tileIds.isEmpty();\n    }\n\n    @Override\n    public MapboxVectorTile next() {\n      var tileId = tileIds.poll();\n      var tile = getTile(tileId);\n      tile.setTileId(tileId);\n      return tile;\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/TestSettings.java",
    "content": "package org.maplibre.mlt;\n\nimport java.nio.file.Paths;\nimport java.util.List;\n\npublic class TestSettings {\n  public static final String OMT_MVT_PATH =\n      Paths.get(\"..\", \"..\", \"test\", \"fixtures\", \"omt\").toString();\n  public static final String BING_MVT_PATH =\n      Paths.get(\"..\", \"..\", \"test\", \"fixtures\", \"bing\").toString();\n  public static final String AMZ_HERE_MVT_PATH =\n      Paths.get(\"..\", \"..\", \"test\", \"fixtures\", \"amazon_here\").toString();\n  public static final List<String> OPTIMIZED_MVT_LAYERS =\n      List.of(\n          \"place\",\n          \"water_name\",\n          \"transportation\",\n          \"transportation_name\",\n          \"park\",\n          \"mountain_peak\",\n          \"poi\",\n          \"waterway\",\n          \"aerodrome_label\",\n          \"water_feature\",\n          \"island\",\n          \"country_region\",\n          \"populated_place\",\n          \"admin_division1\",\n          \"national_park\",\n          \"housenumber\",\n          \"continent\",\n          \"sov_capital\",\n          \"road\",\n          \"water\",\n          \"landuse\",\n          \"landcover\",\n          \"boundary\");\n  /* Layers with ids which are unique per tile but contain no global information, so they can be reassigned\n   *  by the converter */\n  public static final List<String> ID_REASSIGNABLE_MVT_LAYERS =\n      List.of(\"transportation\", \"housenumber\");\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/TestUtils.java",
    "content": "package org.maplibre.mlt;\n\nimport java.util.Map;\nimport java.util.SequencedCollection;\nimport java.util.stream.Collectors;\nimport org.jetbrains.annotations.NotNull;\nimport org.maplibre.mlt.compare.CompareHelper;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MVTFeature;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.util.StreamUtil;\n\npublic class TestUtils {\n  public enum Optimization {\n    NONE,\n    SORTED,\n    IDS_REASSIGNED\n  }\n\n  private static int compareFeatures(\n      SequencedCollection<Feature> mltFeatures,\n      SequencedCollection<Feature> mvtFeatures,\n      boolean allowFeatureSort) {\n    final var difference =\n        CompareHelper.compareFeatures(\n            mltFeatures, mvtFeatures, CompareHelper.CompareMode.All, 0, \"test\", allowFeatureSort);\n    return difference.isPresent() ? 1 : 0;\n  }\n\n  public static int compareTilesSequential(\n      MapLibreTile mlTile, MapboxVectorTile mvTile, boolean allowFeatureSort) {\n    return StreamUtil.zip(\n            mlTile.getLayerStream(),\n            mvTile.getLayerStream(),\n            (mltLayer, mvtLayer) ->\n                compareFeatures(mltLayer.features(), mvtLayer.features(), allowFeatureSort))\n        .reduce(0, Integer::sum);\n  }\n\n  public static interface TileFilter {\n    default boolean test(Layer layer, Feature feature, String propertyKey, Object propertyValue) {\n      return true;\n    }\n\n    default boolean test(Layer layer) {\n      return true;\n    }\n\n    default boolean test(Layer layer, Feature feature) {\n      return true;\n    }\n  }\n\n  // Filter a tile by layer, feature, and/or property.\n  public static MapboxVectorTile filterTile(\n      @NotNull MapboxVectorTile tile, @NotNull TileFilter filter) {\n    return new MapboxVectorTile(\n        tile.getLayerStream()\n            .filter(layer -> filter.test(layer))\n            .map(layer -> filterLayers(layer, filter))\n            .toList(),\n        tile.tileId());\n  }\n\n  private static @NotNull Layer filterLayers(@NotNull Layer layer, @NotNull TileFilter filter) {\n    return new Layer(\n        layer.name(),\n        layer.features().stream()\n            .filter(feature -> filter.test(layer, feature))\n            .flatMap(StreamUtil.ofType(MVTFeature.class))\n            .map(feature -> filterFeatures(layer, feature, filter))\n            .toList(),\n        layer.tileExtent());\n  }\n\n  private static @NotNull Feature filterFeatures(\n      @NotNull Layer layer, @NotNull MVTFeature feature, @NotNull TileFilter filter) {\n    return feature.toBuilder()\n        .properties(\n            feature.getRawProperties().entrySet().stream()\n                .filter(p -> filter.test(layer, feature, p.getKey(), p.getValue()))\n                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))\n        .build();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/benchmarks/CompressionBenchmarksTest.java",
    "content": "package org.maplibre.mlt.benchmarks;\n\nimport static org.maplibre.mlt.TestSettings.ID_REASSIGNABLE_MVT_LAYERS;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.decoder.MltDecoder;\n\n/*\n * Add the tiles which should be benchmarked to the respective directories.\n * */\n@Tag(\"benchmark\")\npublic class CompressionBenchmarksTest {\n  private static final String OMT_PATH = \"../../test/fixtures/omt\";\n  public static final String PLACEHOLDER_FILE = \".gitkeep\";\n\n  @ParameterizedTest\n  @ValueSource(booleans = {false, true})\n  public void omtCompressionBenchmarks_Sort(boolean sorting) throws IOException {\n    var results = runBenchmarks(OMT_PATH, sorting, List.of(), false);\n    if (results == null) {\n      return;\n    }\n\n    System.out.printf(\"Omt sorted tile size reduction: %s%n\", results.getMiddle());\n    System.out.printf(\"Omt sorted compression ratio: %s%% %n\", results.getRight());\n    System.out.printf(\"Omt sorted max tile size reduction: %s%n\", results.getLeft());\n  }\n\n  @ParameterizedTest\n  @ValueSource(booleans = {false, true})\n  public void omtCompressionBenchmarks_OptimizedIds(boolean tessellate) throws IOException {\n    var results = runBenchmarks(OMT_PATH, true, ID_REASSIGNABLE_MVT_LAYERS, tessellate);\n    if (results == null) {\n      return;\n    }\n\n    System.out.printf(\"Omt optimized ids tile size reduction: %s%n\", results.getMiddle());\n    System.out.printf(\"Omt optimized ids compression ratio: %s%% %n\", results.getRight());\n    System.out.printf(\"Omt optimized ids max tile size reduction: %s%n\", results.getLeft());\n  }\n\n  private static Triple<Double, Double, Double> runBenchmarks(\n      @SuppressWarnings(\"SameParameterValue\") String path,\n      boolean allowSorting,\n      List<String> reassignableLayers,\n      boolean tessellate)\n      throws IOException {\n    File bingDirectory = new File(path);\n    File[] files = bingDirectory.listFiles();\n    Assert.isTrue(files != null);\n\n    var tileSizes = new ArrayList<Pair<Integer, Integer>>();\n    var tiles =\n        Arrays.stream(files)\n            .filter(file -> file.isFile() && !file.getName().equals(PLACEHOLDER_FILE))\n            .toList();\n    if (tiles.isEmpty()) {\n      System.out.printf(\"No tiles found in directory %s\\n\", path);\n      return null;\n    }\n\n    for (File tile : tiles) {\n      var tilePath = tile.getAbsolutePath();\n      var sizes =\n          getBenchmarksAndVerifyTiles(tilePath, allowSorting, reassignableLayers, tessellate);\n      tileSizes.add(sizes);\n    }\n\n    var totalMltSizes = 0d;\n    var totalMvtSizes = 0d;\n    var maxReduction = 0d;\n    for (var sizes : tileSizes) {\n      totalMltSizes += sizes.getLeft();\n      totalMvtSizes += sizes.getRight();\n\n      var reduction = (1 - (double) sizes.getLeft() / sizes.getRight()) * 100;\n      if (reduction > maxReduction) {\n        maxReduction = reduction;\n      }\n    }\n\n    var averageMltTileSize = totalMltSizes / tileSizes.size();\n    var averageMvtTileSize = totalMvtSizes / tileSizes.size();\n\n    var averageReduction = (1 - averageMltTileSize / averageMvtTileSize) * 100;\n    var averageRatio = averageMvtTileSize / averageMltTileSize;\n    return Triple.of(maxReduction, averageReduction, averageRatio);\n  }\n\n  private static Pair<Integer, Integer> getBenchmarksAndVerifyTiles(\n      String tilePath, boolean allowSorting, List<String> reassignableLayers, boolean tessellate)\n      throws IOException {\n    var mvtFilePath = Paths.get(tilePath);\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var columnMapping = new ColumnMapping(\"name\", \":\", true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(columnMapping));\n    final var isIdPresent = true;\n    final var tileMetadata =\n        MltConverter.createTilesetMetadata(mvTile, columnMappings, isIdPresent);\n\n    var optimization = new FeatureTableOptimizations(allowSorting, false, List.of(columnMapping));\n    var optimizations =\n        TestSettings.OPTIMIZED_MVT_LAYERS.stream()\n            .collect(Collectors.toMap(l -> l, l -> optimization));\n    for (var reassignableLayer : reassignableLayers) {\n      optimizations.put(\n          reassignableLayer,\n          new FeatureTableOptimizations(allowSorting, true, List.of(columnMapping)));\n    }\n\n    final var config =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(true)\n            .useFSST(true)\n            .preTessellatePolygons(tessellate)\n            .optimizations(optimizations)\n            .build();\n    final var mlTile = MltConverter.encode(mvTile, tileMetadata, config, null);\n\n    if (reassignableLayers.isEmpty()) {\n      /* Only test when the ids are not reassigned since it is verified based on the other tests */\n      var decodedMlt = MltDecoder.decodeMlTile(mlTile);\n      System.out.println(\"Vectorized Decoding not implemented\");\n    }\n\n    var mvtSize = Files.readAllBytes(mvtFilePath).length;\n    return Pair.of(mlTile.length, mvtSize);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/benchmarks/MltDecoderBenchmarkTest.java",
    "content": "package org.maplibre.mlt.benchmarks;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.decoder.MltDecoder;\n\n/**\n * Quick and dirty benchmarks for the decoding of OpenMapTiles schema based tiles into the MVT and\n * MLT in-memory representations. Can be used for simple profiling. For more proper benchmarks based\n * on JMH see `OmtDecoderBenchmark`\n */\n@Tag(\"benchmark\")\npublic class MltDecoderBenchmarkTest {\n\n  /**\n   * Number of measured iterations. Set via {@code -Dbenchmark.iterations=200} for full benchmarks.\n   * Defaults to 1 for a quick smoke test. When greater than 1, an equal number of warmup iterations\n   * are run before measurement.\n   */\n  private static final int BENCHMARK_ITERATIONS = Integer.getInteger(\"benchmark.iterations\", 1);\n\n  @Test\n  public void decodeMlTileVectorized_Z2() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 2, 2, 2);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z3() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 3, 4, 5);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z4() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 4, 8, 10);\n    benchmarkDecoding(tileId);\n\n    var tileId2 = String.format(\"%s_%s_%s\", 4, 3, 9);\n    benchmarkDecoding(tileId2);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z5() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 5, 16, 21);\n    benchmarkDecoding(tileId);\n\n    var tileId2 = String.format(\"%s_%s_%s\", 5, 16, 20);\n    benchmarkDecoding(tileId2);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z6() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 6, 33, 42);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z7() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 7, 66, 85);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z8() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 8, 132, 170);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z9() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 9, 265, 341);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z10() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 10, 532, 682);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z11() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 11, 1064, 1367);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z12() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 12, 2132, 2734);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z13() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 13, 4265, 5467);\n    benchmarkDecoding(tileId);\n  }\n\n  @Test\n  public void decodeMlTileVectorized_Z14() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 14, 8298, 10748);\n    benchmarkDecoding(tileId);\n\n    var tileId2 = String.format(\"%s_%s_%s\", 14, 8299, 10748);\n    benchmarkDecoding(tileId2);\n  }\n\n  @Test\n  public void benchmarkSuite() throws IOException {\n    System.out.println(\"Zoom 2 ---------------------------------------\");\n    decodeMlTileVectorized_Z2();\n    System.out.println(\"Zoom 3 ---------------------------------------\");\n    decodeMlTileVectorized_Z3();\n    System.out.println(\"Zoom 4 ---------------------------------------\");\n    decodeMlTileVectorized_Z4();\n    System.out.println(\"Zoom 5 ---------------------------------------\");\n    decodeMlTileVectorized_Z5();\n    System.out.println(\"Zoom 6 ---------------------------------------\");\n    decodeMlTileVectorized_Z6();\n    System.out.println(\"Zoom 7 ---------------------------------------\");\n    decodeMlTileVectorized_Z7();\n    System.out.println(\"Zoom 8 ---------------------------------------\");\n    decodeMlTileVectorized_Z8();\n    System.out.println(\"Zoom 9 ---------------------------------------\");\n    decodeMlTileVectorized_Z9();\n    System.out.println(\"Zoom 10 ---------------------------------------\");\n    decodeMlTileVectorized_Z10();\n    System.out.println(\"Zoom 11 ---------------------------------------\");\n    decodeMlTileVectorized_Z11();\n    System.out.println(\"Zoom 12 ---------------------------------------\");\n    decodeMlTileVectorized_Z12();\n    System.out.println(\"Zoom 13 ---------------------------------------\");\n    decodeMlTileVectorized_Z13();\n    System.out.println(\"Zoom 14 ---------------------------------------\");\n    decodeMlTileVectorized_Z14();\n  }\n\n  private void benchmarkDecoding(String tileId) throws IOException {\n    var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    int warmup_iters = BENCHMARK_ITERATIONS / 2;\n\n    var mvt = Files.readAllBytes(mvtFilePath);\n    var mvtTimeElapsed = 0L;\n    for (int i = 0; i < BENCHMARK_ITERATIONS; i++) {\n      long start = System.currentTimeMillis();\n      var mvTile = MvtUtils.decodeMvtFast(mvt);\n      long finish = System.currentTimeMillis();\n\n      if (i > warmup_iters) {\n        mvtTimeElapsed += (finish - start);\n      }\n    }\n    if (BENCHMARK_ITERATIONS > 1) {\n      System.out.println(\"MVT decoding time: \" + (mvtTimeElapsed / (double) warmup_iters));\n    } else {\n      System.out.println(\"MVT decoding passed (single iter)\");\n    }\n\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var columnMapping = new ColumnMapping(\"name\", \":\", true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(columnMapping));\n    final var tileMetadata = MltConverter.createTilesetMetadata(mvTile, columnMappings, true);\n\n    var allowIdRegeneration = true;\n    var allowSorting = false;\n    var optimization =\n        new FeatureTableOptimizations(allowSorting, allowIdRegeneration, List.of(columnMapping));\n    // TODO: fix -> either add columMappings per layer or global like when creating the scheme\n    var optimizations =\n        Map.of(\n            \"place\",\n            optimization,\n            \"water_name\",\n            optimization,\n            \"transportation\",\n            optimization,\n            \"transportation_name\",\n            optimization,\n            \"park\",\n            optimization,\n            \"mountain_peak\",\n            optimization,\n            \"poi\",\n            optimization,\n            \"waterway\",\n            optimization,\n            \"aerodrome_label\",\n            optimization);\n    var mlTile =\n        MltConverter.encode(\n            mvTile,\n            tileMetadata,\n            ConversionConfig.builder()\n                .includeIds(true)\n                .useFastPFOR(true)\n                .useFSST(true)\n                .optimizations(optimizations)\n                .build(),\n            null);\n\n    var mltTimeElapsed = 0L;\n    for (int i = 0; i < BENCHMARK_ITERATIONS; i++) {\n      long start = System.currentTimeMillis();\n      var decodedTile = MltDecoder.decodeMlTile(mlTile);\n      long finish = System.currentTimeMillis();\n\n      if (i > warmup_iters) {\n        mltTimeElapsed += (finish - start);\n      }\n    }\n    if (BENCHMARK_ITERATIONS > 1) {\n      System.out.println(\"MLT decoding time: \" + (mltTimeElapsed / (double) warmup_iters));\n    } else {\n      System.out.println(\"MLT decoding passed (single iter)\");\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/compare/CompareHelperTest.java",
    "content": "package org.maplibre.mlt.compare;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.maplibre.mlt.compare.CompareHelper.CompareMode;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MVTFeature;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.MapboxVectorTile;\n\nclass CompareHelperTest {\n  @Test\n  void identicalEmptyTilesAreEqual() {\n    final var result = CompareHelper.compareTiles(mltOf(), mvtOf(), CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void identicalTilesWithFeaturesAreEqual() {\n    final var layer = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var result = CompareHelper.compareTiles(mltOf(layer), mvtOf(layer), CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void identicalTilesWithMultipleLayersAreEqual() {\n    final var l1 = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var l2 = createLayer(\"water\", createPointFeature(2, Map.of(\"class\", \"river\")));\n    final var result = CompareHelper.compareTiles(mltOf(l1, l2), mvtOf(l1, l2), CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void differentLayerCountIsDetected() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of())));\n    final var mvt =\n        mvtOf(\n            createLayer(\"roads\", createPointFeature(1, Map.of())),\n            createLayer(\"water\", createPointFeature(2, Map.of())));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Number of layers\"));\n  }\n\n  @Test\n  void emptyMvtLayersAreIgnoredInLayerCount() {\n    final var emptyLayer = createLayer(\"empty\");\n    final var roadsLayer = createLayer(\"roads\", createPointFeature(1, Map.of()));\n    final var mvt = mvtOf(emptyLayer, roadsLayer);\n    final var mlt = mltOf(roadsLayer);\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void differentLayerNames() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of())));\n    final var mvt = mvtOf(createLayer(\"water\", createPointFeature(1, Map.of())));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Layer names differ\"));\n  }\n\n  @Test\n  void differentFeatureCountInLayer() {\n    final var mlt =\n        mltOf(\n            createLayer(\"roads\", createPointFeature(1, Map.of()), createPointFeature(2, Map.of())));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of())));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Number of features differ\"));\n  }\n\n  @Test\n  void differentFeatureIds() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(99, Map.of())));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of())));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Feature IDs differ\"));\n  }\n\n  @Test\n  void featureWithIdVsFeatureWithoutIdDiffer() {\n    final var withId = createLayer(\"roads\", createPointFeature(1, Map.of()));\n    final var withoutId = createLayer(\"roads\", createPointFeature(Map.of()));\n    final var result = CompareHelper.compareTiles(mltOf(withoutId), mvtOf(withId), CompareMode.All);\n    assertTrue(result.isPresent());\n  }\n\n  @Test\n  void featuresWithoutIdAreEqual() {\n    final var withId = createLayer(\"roads\", createPointFeature(Map.of()));\n    final var withoutId = createLayer(\"roads\", createPointFeature(Map.of()));\n    final var result = CompareHelper.compareTiles(mltOf(withoutId), mvtOf(withId), CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void differentGeometriesInGeometryMode() {\n    final var point1 = FACTORY.createPoint(new Coordinate(1, 2));\n    final var point2 = FACTORY.createPoint(new Coordinate(3, 4));\n    final var mlt = mltOf(createLayer(\"roads\", createFeature(1, point1)));\n    final var mvt = mvtOf(createLayer(\"roads\", createFeature(1, point2)));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Geometry);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Geometries do not match\"));\n  }\n\n  @Test\n  void geometryDifferencesAreNotCheckedInLayersOnlyMode() {\n    final var point1 = FACTORY.createPoint(new Coordinate(1, 2));\n    final var point2 = FACTORY.createPoint(new Coordinate(3, 4));\n    final var mlt = mltOf(createLayer(\"roads\", createFeature(1, point1)));\n    final var mvt = mvtOf(createLayer(\"roads\", createFeature(1, point2)));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Layers);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void geometryDifferencesAreNotCheckedInPropertiesMode() {\n    final var point1 = FACTORY.createPoint(new Coordinate(1, 2));\n    final var point2 = FACTORY.createPoint(new Coordinate(3, 4));\n    final var mlt = mltOf(createLayer(\"roads\", createFeature(1, point1)));\n    final var mvt = mvtOf(createLayer(\"roads\", createFeature(1, point2)));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void differentPropertyKeys() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\"))));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"ref\", \"M1\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Property keys do not match\"));\n  }\n\n  @Test\n  void differentPropertyValues() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Side St\"))));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(result.isPresent());\n    assertTrue(result.get().toString().contains(\"Property values do not match\"));\n  }\n\n  @Test\n  void propertyDifferencesNotCheckedInGeometryMode() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Side St\"))));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Geometry);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void nullPropertyValueInMltTreatedAsAbsent() {\n    final Map<String, Object> mltProps = new java.util.HashMap<>();\n    mltProps.put(\"name\", null);\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, mltProps)));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of())));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void numericValuesWithSameStringRepresentationEqual() {\n    final var mltFeature = createPointFeature(1, Map.of(\"pop\", 42L));\n    final var mvtFeature = createPointFeature(1, Map.of(\"pop\", 42));\n    final var mlt = mltOf(createLayer(\"roads\", mltFeature));\n    final var mvt = mvtOf(createLayer(\"roads\", mvtFeature));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void layerFilterIncludesOnlyMatchingLayers() {\n    final var roadsMlt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var roadsMvt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var waterMvt = createLayer(\"water\", createPointFeature(2, Map.of(\"name\", \"wrong\")));\n    final var waterMlt = createLayer(\"water\", createPointFeature(2, Map.of(\"name\", \"wrong\")));\n    final var result =\n        CompareHelper.compareTiles(\n            mltOf(roadsMlt, waterMlt),\n            mvtOf(roadsMvt, waterMvt),\n            CompareMode.All,\n            Pattern.compile(\"roads\"),\n            false);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void layerFilterExcludesMatchingLayersWhenInverted() {\n    final var roadsMlt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var roadsMvt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var waterMvt = createLayer(\"water\", createPointFeature(2, Map.of(\"name\", \"wrong\")));\n    final var waterMlt = createLayer(\"water\", createPointFeature(2, Map.of(\"name\", \"wrong\")));\n    final var result =\n        CompareHelper.compareTiles(\n            mltOf(roadsMlt, waterMlt),\n            mvtOf(roadsMvt, waterMvt),\n            CompareMode.All,\n            Pattern.compile(\"water\"),\n            true);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void layerFilterDetectsDifferenceInMatchingLayer() {\n    final var roadsMlt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Side St\")));\n    final var roadsMvt = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var mlt = mltOf(roadsMlt);\n    final var mvt = mvtOf(roadsMvt);\n    final var result =\n        CompareHelper.compareTiles(mlt, mvt, CompareMode.All, Pattern.compile(\"roads\"), false);\n    assertTrue(result.isPresent());\n  }\n\n  @Test\n  void nullLayerFilterComparesAllLayers() {\n    final var l1 = createLayer(\"roads\", createPointFeature(1, Map.of(\"name\", \"Main St\")));\n    final var l2 = createLayer(\"water\", createPointFeature(2, Map.of()));\n    final var result =\n        CompareHelper.compareTiles(mltOf(l1, l2), mvtOf(l1, l2), CompareMode.All, null, false);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void differenceMessageIncludesLayerName() {\n    final var mlt = mltOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"x\", \"a\"))));\n    final var mvt = mvtOf(createLayer(\"roads\", createPointFeature(1, Map.of(\"x\", \"b\"))));\n    final var diff = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(diff.isPresent());\n    assertTrue(diff.get().toString().contains(\"roads\"));\n  }\n\n  @Test\n  void differenceMessageIncludesFeatureIndex() {\n    final var mlt =\n        mltOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(1, Map.of(\"x\", \"ok\"), 0),\n                createPointFeature(2, Map.of(\"x\", \"bad\"), 1)));\n    final var mvt =\n        mvtOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(2, Map.of(\"x\", \"good\"), 1),\n                createPointFeature(1, Map.of(\"x\", \"ok\"), 0)));\n\n    // Features sorted by id, difference at index 1\n    final var diff = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(diff.isPresent());\n    assertTrue(diff.get().toString().contains(\"1\"));\n  }\n\n  @Test\n  void featuresWithIdsMatchEvenWhenOrderDiffers() {\n    final var mlt =\n        mltOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(1, Map.of(\"name\", \"first\")),\n                createPointFeature(2, Map.of(\"name\", \"second\"))));\n    final var mvt =\n        mvtOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(2, Map.of(\"name\", \"second\")),\n                createPointFeature(1, Map.of(\"name\", \"first\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.All);\n    assertFalse(result.isPresent());\n  }\n\n  @Test\n  void featuresWithoutIdsDoNotMatchWhenOrderDiffers() {\n    final var mlt =\n        mltOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(Map.of(\"name\", \"first\")),\n                createPointFeature(Map.of(\"name\", \"second\"))));\n    final var mvt =\n        mvtOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(Map.of(\"name\", \"second\")),\n                createPointFeature(Map.of(\"name\", \"first\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(result.isPresent());\n  }\n\n  @Test\n  void sortingByIdStillDetectsPropertyDifferences() {\n    final var mlt =\n        mltOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(1, Map.of(\"name\", \"first\")),\n                createPointFeature(2, Map.of(\"name\", \"second\"))));\n    final var mvt =\n        mvtOf(\n            createLayer(\n                \"roads\",\n                createPointFeature(2, Map.of(\"name\", \"wrong\")),\n                createPointFeature(1, Map.of(\"name\", \"first\"))));\n    final var result = CompareHelper.compareTiles(mlt, mvt, CompareMode.Properties);\n    assertTrue(result.isPresent());\n  }\n\n  private static MapLibreTile mltOf(@NotNull Layer... layers) {\n    return new MapLibreTile(List.of(layers));\n  }\n\n  private static MapboxVectorTile mvtOf(@NotNull Layer... layers) {\n    return new MapboxVectorTile(List.of(layers));\n  }\n\n  private static Layer createLayer(@NotNull String name, @NotNull MVTFeature... features) {\n    return new Layer(name, List.of(features), 4096);\n  }\n\n  private static MVTFeature createPointFeature(long id, Map<String, Object> props) {\n    return createPointFeature(id, props, 0);\n  }\n\n  private static MVTFeature createPointFeature(long id, Map<String, Object> props, int index) {\n    return MVTFeature.builder()\n        .index(index)\n        .id(id)\n        .geometry(FACTORY.createPoint(new Coordinate(1, 2)))\n        .properties(props)\n        .build();\n  }\n\n  private static MVTFeature createFeature(long id, Geometry geom) {\n    return createFeature(id, geom, 0);\n  }\n\n  private static MVTFeature createFeature(long id, Geometry geom, int index) {\n    return MVTFeature.builder().index(index).id(id).geometry(geom).properties(Map.of()).build();\n  }\n\n  private static MVTFeature createPointFeature(Map<String, Object> props) {\n    return createPointFeature(props, 0);\n  }\n\n  private static MVTFeature createPointFeature(Map<String, Object> props, int index) {\n    return MVTFeature.builder()\n        .index(index)\n        .geometry(FACTORY.createPoint(new Coordinate(1, 2)))\n        .properties(props)\n        .build();\n  }\n\n  private static final GeometryFactory FACTORY = new GeometryFactory();\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/ConversionConfigTest.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport org.junit.jupiter.api.Test;\n\npublic class ConversionConfigTest {\n\n  private static Map<String, FeatureTableOptimizations> sampleOptimizations() {\n    var m = new HashMap<String, FeatureTableOptimizations>();\n    m.put(\"layerA\", new FeatureTableOptimizations(true, false, List.of()));\n    return m;\n  }\n\n  private static void assertConfigEquals(ConversionConfig expected, ConversionConfig actual) {\n    assertEquals(expected.includeIds(), actual.includeIds(), \"includeIds\");\n    assertEquals(expected.useFastPFOR(), actual.useFastPFOR(), \"useFastPFOR\");\n    assertEquals(expected.useFSST(), actual.useFSST(), \"useFSST\");\n    assertEquals(expected.typeMismatchPolicy(), actual.typeMismatchPolicy(), \"typeMismatchPolicy\");\n    assertEquals(expected.optimizations(), actual.optimizations(), \"optimizations\");\n    assertEquals(expected.useMortonEncoding(), actual.useMortonEncoding(), \"useMortonEncoding\");\n    assertEquals(\n        expected.preTessellatePolygons(), actual.preTessellatePolygons(), \"preTessellatePolygons\");\n    assertEquals(expected.outlineFeatureTableNames(), actual.outlineFeatureTableNames(), \"outline\");\n\n    var expPattern = expected.layerFilterPattern();\n    var actPattern = actual.layerFilterPattern();\n    if (expPattern == null) {\n      assertNull(actPattern, \"layerFilterPattern\");\n    } else {\n      assertNotNull(actPattern, \"layerFilterPattern-not-null\");\n      assertEquals(expPattern.pattern(), actPattern.pattern(), \"layerFilterPattern.value\");\n    }\n\n    assertEquals(expected.layerFilterInvert(), actual.layerFilterInvert(), \"layerFilterInvert\");\n    assertEquals(\n        expected.integerEncodingOption(), actual.integerEncodingOption(), \"integerEncoding\");\n    assertEquals(\n        expected.geometryEncodingOption(), actual.geometryEncodingOption(), \"geometryEncoding\");\n  }\n\n  @Test\n  public void testDefaults_fromNoArgConstructor() {\n    var cfg = ConversionConfig.builder().build();\n\n    assertEquals(ConversionConfig.DEFAULT_INCLUDE_IDS, cfg.includeIds());\n    assertEquals(ConversionConfig.DEFAULT_USE_FAST_PFOR, cfg.useFastPFOR());\n    assertEquals(ConversionConfig.DEFAULT_USE_FSST, cfg.useFSST());\n    assertEquals(ConversionConfig.DEFAULT_MISMATCH_POLICY, cfg.typeMismatchPolicy());\n    assertNotNull(cfg.optimizations(), \"optimizations-not-null\");\n    assertTrue(cfg.optimizations().isEmpty(), \"optimizations-empty\");\n    assertEquals(ConversionConfig.DEFAULT_USE_MORTON_ENCODING, cfg.useMortonEncoding());\n    assertEquals(ConversionConfig.DEFAULT_PRE_TESSELLATE_POLYGONS, cfg.preTessellatePolygons());\n    assertNotNull(cfg.outlineFeatureTableNames(), \"outline-not-null\");\n    assertTrue(cfg.outlineFeatureTableNames().isEmpty(), \"outline-empty\");\n    assertNull(cfg.layerFilterPattern(), \"layerFilterPattern-default-null\");\n    assertEquals(ConversionConfig.DEFAULT_LAYER_FILTER_INVERT, cfg.layerFilterInvert());\n    assertEquals(ConversionConfig.DEFAULT_INTEGER_ENCODING, cfg.integerEncodingOption());\n    assertEquals(ConversionConfig.DEFAULT_INTEGER_ENCODING, cfg.geometryEncodingOption());\n  }\n\n  @Test\n  public void testConstructor_preservesPassedOptimizationsAndOutline() {\n    var optim = sampleOptimizations();\n    var outline = List.of(\"layerA\");\n\n    var cfg =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(false)\n            .useFSST(false)\n            .typeMismatchPolicy(ConversionConfig.TypeMismatchPolicy.ELIDE)\n            .optimizations(optim)\n            .preTessellatePolygons(false)\n            .useMortonEncoding(true)\n            .outlineFeatureTableNames(outline)\n            .layerFilterPattern(Pattern.compile(\"layerA\"))\n            .layerFilterInvert(false)\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.PLAIN)\n            .geometryEncodingOption(ConversionConfig.IntegerEncodingOption.PLAIN)\n            .build();\n\n    assertSame(optim, cfg.optimizations(), \"optimizations-same-reference\");\n    assertEquals(outline, cfg.outlineFeatureTableNames(), \"outline-equals\");\n  }\n\n  @Test\n  public void testBuilder_setsAllFields_andBuildProducesEquivalentConfig() {\n    var optim = sampleOptimizations();\n    var outline = List.of(\"layerA\");\n    var pattern = Pattern.compile(\"^layer.*$\");\n\n    var built =\n        ConversionConfig.builder()\n            .includeIds(false)\n            .useFastPFOR(true)\n            .useFSST(true)\n            .typeMismatchPolicy(ConversionConfig.TypeMismatchPolicy.COERCE)\n            .optimizations(optim)\n            .preTessellatePolygons(true)\n            .useMortonEncoding(false)\n            .outlineFeatureTableNames(outline)\n            .layerFilterPattern(pattern)\n            .layerFilterInvert(true)\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.DELTA)\n            .build();\n\n    assertEquals(false, built.includeIds());\n    assertEquals(true, built.useFastPFOR());\n    assertEquals(true, built.useFSST());\n    assertEquals(ConversionConfig.TypeMismatchPolicy.COERCE, built.typeMismatchPolicy());\n    assertEquals(optim, built.optimizations());\n    assertEquals(true, built.preTessellatePolygons());\n    assertEquals(false, built.useMortonEncoding());\n    assertEquals(outline, built.outlineFeatureTableNames());\n    assertNotNull(built.layerFilterPattern());\n    assertEquals(pattern.pattern(), built.layerFilterPattern().pattern());\n    assertEquals(true, built.layerFilterInvert());\n    assertEquals(ConversionConfig.IntegerEncodingOption.DELTA, built.integerEncodingOption());\n  }\n\n  @Test\n  public void testAsBuilder_roundTrip() {\n    var orig =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(true)\n            .useFSST(false)\n            .typeMismatchPolicy(ConversionConfig.TypeMismatchPolicy.ELIDE)\n            .optimizations(sampleOptimizations())\n            .preTessellatePolygons(false)\n            .useMortonEncoding(true)\n            .outlineFeatureTableNames(List.of(\"layerA\"))\n            .layerFilterPattern(Pattern.compile(\"layerA\"))\n            .layerFilterInvert(false)\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.PLAIN)\n            .build();\n\n    var rebuilt = orig.toBuilder().build();\n    assertConfigEquals(orig, rebuilt);\n  }\n\n  @Test\n  public void testLayerFilterPatternAndInvert_behavior() {\n    var pattern = Pattern.compile(\"layerA\");\n    var cfg =\n        ConversionConfig.builder().layerFilterPattern(pattern).layerFilterInvert(true).build();\n    assertNotNull(cfg.layerFilterPattern());\n    assertEquals(\"layerA\", cfg.layerFilterPattern().pattern());\n    assertTrue(cfg.layerFilterInvert());\n\n    var cfg2 = ConversionConfig.builder().layerFilterPattern(null).layerFilterInvert(false).build();\n    assertNull(cfg2.layerFilterPattern());\n    assertFalse(cfg2.layerFilterInvert());\n  }\n\n  @Test\n  public void testIntegerEncodingOption_defaultsAndCustom() {\n    var defaultCfg = ConversionConfig.builder().build();\n    assertEquals(ConversionConfig.DEFAULT_INTEGER_ENCODING, defaultCfg.integerEncodingOption());\n\n    var custom =\n        ConversionConfig.builder()\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.RLE)\n            .build();\n    assertEquals(ConversionConfig.IntegerEncodingOption.RLE, custom.integerEncodingOption());\n  }\n\n  @Test\n  public void testGeometryEncodingOption_defaultsAndCustom() {\n    var defaultCfg = ConversionConfig.builder().build();\n    assertEquals(ConversionConfig.DEFAULT_INTEGER_ENCODING, defaultCfg.geometryEncodingOption());\n\n    var custom =\n        ConversionConfig.builder()\n            .geometryEncodingOption(ConversionConfig.IntegerEncodingOption.PLAIN)\n            .build();\n    assertEquals(ConversionConfig.IntegerEncodingOption.PLAIN, custom.geometryEncodingOption());\n\n    // When only integer encoding is set, geometry stays AUTO (backward compatible with main:\n    // on main, geometry streams always used AUTO and were not controlled by integerEncodingOption).\n    var withIntegerOnly =\n        ConversionConfig.builder()\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.RLE)\n            .build();\n    assertEquals(\n        ConversionConfig.IntegerEncodingOption.RLE, withIntegerOnly.integerEncodingOption());\n    assertEquals(\n        ConversionConfig.DEFAULT_INTEGER_ENCODING, withIntegerOnly.geometryEncodingOption());\n  }\n\n  @Test\n  public void testIntegerEncodingOnlyConstructor_geometryStaysAuto() {\n    // Constructor that takes integerEncodingOption: geometry encoding stays AUTO for backward\n    // compatibility (on main, geometry was never configurable and always used AUTO).\n    var cfg =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(false)\n            .useFSST(false)\n            .optimizations(Map.of())\n            .preTessellatePolygons(false)\n            .integerEncodingOption(ConversionConfig.IntegerEncodingOption.DELTA)\n            .build();\n    assertEquals(ConversionConfig.IntegerEncodingOption.DELTA, cfg.integerEncodingOption());\n    assertEquals(ConversionConfig.DEFAULT_INTEGER_ENCODING, cfg.geometryEncodingOption());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/MltConverterTest.java",
    "content": "package org.maplibre.mlt.converter;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MVTFeature;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\nclass MltConverterTest {\n  @Test\n  void coerceValuesToString() {\n    var metadata =\n        MltConverter.createTilesetMetadata(\n            createTileWithMixedTypes(),\n            new ColumnMappingConfig(),\n            true,\n            ConversionConfig.TypeMismatchPolicy.COERCE);\n    var column =\n        metadata.featureTables.stream()\n            .flatMap(it -> it.columns().stream())\n            .filter(it -> \"key\".equals(it.getName()))\n            .findFirst()\n            .get();\n    assertEquals(MltMetadata.ScalarType.STRING, column.field().type().scalarType().physicalType());\n  }\n\n  @Test\n  void elideValues() {\n    var metadata =\n        MltConverter.createTilesetMetadata(\n            createTileWithMixedTypes(),\n            new ColumnMappingConfig(),\n            true,\n            ConversionConfig.TypeMismatchPolicy.ELIDE);\n    var column =\n        metadata.featureTables.stream()\n            .flatMap(it -> it.columns().stream())\n            .filter(it -> \"key\".equals(it.getName()))\n            .findFirst()\n            .get();\n    assertEquals(MltMetadata.ScalarType.DOUBLE, column.field().type().scalarType().physicalType());\n  }\n\n  @Test\n  void failOnMismatchedValues() {\n    assertThrows(\n        RuntimeException.class,\n        () -> {\n          MltConverter.createTilesetMetadata(\n              createTileWithMixedTypes(), new ColumnMappingConfig(), true);\n        });\n  }\n\n  private static MapboxVectorTile createTileWithMixedTypes() {\n    return new MapboxVectorTile(\n        List.of(\n            new Layer(\n                \"layer\",\n                List.of(\n                    MVTFeature.builder()\n                        .index(0)\n                        .id(1)\n                        .geometry(emptyGeometry)\n                        .properties(Map.of(\"key\", 1.2))\n                        .build(),\n                    MVTFeature.builder()\n                        .index(1)\n                        .id(2)\n                        .geometry(emptyGeometry)\n                        .properties(Map.of(\"key\", \"2\"))\n                        .build(),\n                    MVTFeature.builder()\n                        .index(2)\n                        .id(3)\n                        .geometry(emptyGeometry)\n                        .properties(Map.of(\"key\", 1))\n                        .build()),\n                4096)));\n  }\n\n  @Test\n  void generateMetadataJSON() {\n    final var metadata = new MltMetadata.TileSetMetadata();\n\n    metadata.name = \"test\";\n    metadata.description = \"A test tileset\";\n    metadata.attribution = \"MapLibre\";\n    metadata.bounds = List.of(-180.0, -85.0511287798066, 180.0, 85.0511287798066);\n    metadata.center = List.of(0.0, 0.0);\n    metadata.minZoom = Optional.of(0);\n    metadata.maxZoom = Optional.of(14);\n    metadata.featureTables = List.of();\n\n    MltConverter.createTilesetMetadataJSON(metadata);\n  }\n\n  private static final GeometryFactory factory = new GeometryFactory();\n  private static final Geometry emptyGeometry = factory.createEmpty(2);\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/EncodingUtilsTest.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\n\nimport java.io.IOException;\nimport java.util.BitSet;\nimport java.util.List;\nimport java.util.stream.IntStream;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.decoder.DecodingUtils;\n\npublic class EncodingUtilsTest {\n\n  @Test\n  public void encodeRle_MixedRunsAndLiterals_ValidEncoding() {\n    var values = List.of(1, 1, 1, 2, 4, 5, 8, 8, 8, 8, 9, 9);\n    var expectedRuns = List.of(3, 1, 1, 1, 4, 2);\n    var expectedValues = List.of(1, 2, 4, 5, 8, 9);\n\n    var actualValues = EncodingUtils.encodeRle(values.stream().mapToInt(i -> i).toArray());\n\n    assertEquals(expectedRuns, IntStream.of(actualValues.getLeft()).boxed().toList());\n    assertEquals(expectedValues, IntStream.of(actualValues.getRight()).boxed().toList());\n  }\n\n  @Test\n  public void encodeRle_OnlyLiterals_ValidEncoding() {\n    var values = List.of(1, 2, 3, 4, 5, 6, 7, 8);\n    var expectedRuns = List.of(1, 1, 1, 1, 1, 1, 1, 1);\n    var expectedValues = List.of(1, 2, 3, 4, 5, 6, 7, 8);\n\n    var actualValues = EncodingUtils.encodeRle(values.stream().mapToInt(i -> i).toArray());\n\n    assertEquals(expectedRuns, IntStream.of(actualValues.getLeft()).boxed().toList());\n    assertEquals(expectedValues, IntStream.of(actualValues.getRight()).boxed().toList());\n  }\n\n  @Test\n  public void encodeRle_OnlyRuns_ValidEncoding() {\n    var values = List.of(10, 10, 10, 10, 20, 20, 40, 40, 40, 40);\n    var expectedRuns = List.of(4, 2, 4);\n    var expectedValues = List.of(10, 20, 40);\n\n    var actualValues = EncodingUtils.encodeRle(values.stream().mapToInt(i -> i).toArray());\n\n    assertEquals(expectedRuns, IntStream.of(actualValues.getLeft()).boxed().toList());\n    assertEquals(expectedValues, IntStream.of(actualValues.getRight()).boxed().toList());\n  }\n\n  @Test\n  public void encodeBooleanRle() throws IOException {\n    var numValues = 70;\n    var bitset = new BitSet();\n    for (var i = 0; i < numValues; i++) {\n      bitset.set(i, false);\n    }\n\n    var encodedBooleans = EncodingUtils.encodeBooleanRle(bitset, numValues);\n\n    var decodeBooleans =\n        DecodingUtils.decodeBooleanRle(\n            encodedBooleans, numValues, encodedBooleans.length, new IntWrapper(0));\n\n    for (var i = 0; i < numValues; i++) {\n      assertFalse(decodeBooleans.get(i));\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/LinearRegressionTest.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport static org.maplibre.mlt.converter.encodings.LinearRegression.calculateDeltas;\nimport static org.maplibre.mlt.converter.encodings.LinearRegression.gradientDescent;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.stream.Collectors;\nimport java.util.stream.DoubleStream;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.converter.geometry.HilbertCurve;\nimport org.maplibre.mlt.converter.geometry.Vertex;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\n\npublic class LinearRegressionTest {\n\n  @Test\n  public void test() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 5, 16, 20);\n    var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    for (var layer : mvTile.getLayers()) {\n      if (layer.name().equals(\"transportation\")) {\n        var features = layer.features();\n        var geometries = features.stream().map(f -> f.getGeometry()).collect(Collectors.toList());\n        var vertices =\n            geometries.stream()\n                .map(g -> g.getCoordinates())\n                .flatMap(i -> Stream.of(i[0], i[1]))\n                .collect(Collectors.toList());\n\n        var minVertexValue =\n            Collections.min(\n                vertices.stream()\n                    .flatMapToDouble(v -> DoubleStream.of(v.getX(), v.getY()))\n                    .boxed()\n                    .collect(Collectors.toList()));\n        var maxVertexValue =\n            Collections.max(\n                vertices.stream()\n                    .flatMapToDouble(v -> DoubleStream.of(v.getX(), v.getY()))\n                    .boxed()\n                    .collect(Collectors.toList()));\n        var hilbertCurve = new HilbertCurve(minVertexValue.intValue(), maxVertexValue.intValue());\n        var hilbertIndices =\n            vertices.stream()\n                .mapToInt(i -> hilbertCurve.encode(new Vertex((int) i.getX(), (int) i.getY())))\n                .toArray();\n        var sortedHilbertIndices =\n            Arrays.stream(hilbertIndices).sorted().boxed().collect(Collectors.toList());\n        sortedHilbertIndices =\n            sortedHilbertIndices.stream().distinct().collect(Collectors.toList()).subList(82, 99);\n\n        var deltas =\n            EncodingUtils.encodeDeltas(sortedHilbertIndices.stream().mapToInt(i -> i).toArray());\n\n        double[] indices = IntStream.range(0, deltas.length).boxed().mapToDouble(i -> i).toArray();\n        double alpha = 0.01; // example learning rate\n        int iterations = 1000; // example number of iterations\n        double[] J = new double[iterations]; // to store cost history\n        double[] theta =\n            gradientDescent(\n                indices,\n                sortedHilbertIndices.stream().mapToDouble(i -> i).toArray(),\n                alpha,\n                iterations,\n                J);\n        var deltasLinearRegression =\n            calculateDeltas(\n                indices, sortedHilbertIndices.stream().mapToDouble(i -> i).toArray(), theta);\n        var modifiedDeltasLinearRegression =\n            Arrays.copyOfRange(deltasLinearRegression, 1, deltasLinearRegression.length);\n\n        var modifiedDeltas = Arrays.copyOfRange(deltas, 1, deltas.length);\n        var deltaSum = Arrays.stream(modifiedDeltas).sum();\n        var deltaLinearRegressionSum = Arrays.stream(modifiedDeltasLinearRegression).sum();\n        System.out.println(\n            \"delta: \" + deltaSum + \" linear Regression delta: \" + deltaLinearRegressionSum);\n        System.out.println(\n            \"max delta: \"\n                + Arrays.stream(modifiedDeltas).max().getAsInt()\n                + \" max linear Regression delta: \"\n                + Arrays.stream(modifiedDeltasLinearRegression).max().getAsDouble());\n      }\n    }\n  }\n\n  @Test\n  public void test2() throws IOException {\n    var tileId = String.format(\"%s_%s_%s\", 5, 16, 20);\n    var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    for (var layer : mvTile.getLayers()) {\n      if (layer.name().equals(\"transportation\")) {\n        var features = layer.features();\n        var geometries = features.stream().map(f -> f.getGeometry()).collect(Collectors.toList());\n        var vertices =\n            geometries.stream()\n                .map(g -> g.getCoordinates())\n                .flatMap(i -> Stream.of(i[0], i[1]))\n                .collect(Collectors.toList());\n\n        var xCoordinates =\n            vertices.stream()\n                .mapToInt(i -> (int) i.getX())\n                .distinct()\n                .boxed()\n                .collect(Collectors.toList())\n                .subList(81, 104);\n\n        var deltas = EncodingUtils.encodeDeltas(xCoordinates.stream().mapToInt(i -> i).toArray());\n\n        double[] indices = IntStream.range(0, deltas.length).boxed().mapToDouble(i -> i).toArray();\n        double alpha = 0.01; // example learning rate\n        int iterations = 1000; // example number of iterations\n        double[] J = new double[iterations]; // to store cost history\n        double[] theta =\n            gradientDescent(\n                indices, xCoordinates.stream().mapToDouble(i -> i).toArray(), alpha, iterations, J);\n        var deltasLinearRegression =\n            calculateDeltas(indices, xCoordinates.stream().mapToDouble(i -> i).toArray(), theta);\n        var modifiedDeltasLinearRegression =\n            Arrays.copyOfRange(deltasLinearRegression, 1, deltasLinearRegression.length);\n\n        var modifiedDeltas = Arrays.copyOfRange(deltas, 1, deltas.length);\n        var deltaSum = Arrays.stream(modifiedDeltas).sum();\n        var deltaLinearRegressionSum = Arrays.stream(modifiedDeltasLinearRegression).sum();\n        System.out.println(\n            \"delta: \" + deltaSum + \" linear Regression delta: \" + deltaLinearRegressionSum);\n        System.out.println(\n            \"max delta: \"\n                + Arrays.stream(modifiedDeltas).max().getAsInt()\n                + \" max linear Regression delta: \"\n                + Arrays.stream(modifiedDeltasLinearRegression).max().getAsDouble());\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/MltTypeMapTest.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.List;\nimport java.util.Set;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n/// Test encoding and decoding of column types\npublic class MltTypeMapTest {\n  /// Test that all valid type codes can be decoded and re-encoded to the same value\n  @Test\n  public void roundTrips() {\n    final var valid =\n        Set.of(\n            0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\n            28, 29, 30);\n\n    for (var i = 0; i < 100; ++i) {\n      final MltMetadata.FieldType[] types = new MltMetadata.FieldType[] {null};\n      if (valid.contains(i)) {\n        final var typeCode = i;\n        Assertions.assertDoesNotThrow(\n            () -> types[0] = MltTypeMap.Tag0x01.decodeColumnType(typeCode));\n      } else {\n        final var typeCode = i;\n        Assertions.assertThrows(\n            IllegalStateException.class,\n            () -> {\n              types[0] = MltTypeMap.Tag0x01.decodeColumnType(typeCode);\n            });\n        continue;\n      }\n\n      var column = types[0];\n\n      // STRUCT must have children before being re-encoded\n      if (MltTypeMap.Tag0x01.columnTypeHasChildren(i)) {\n        Assertions.assertNotNull(column.complexType());\n        Assertions.assertNotNull(column.complexType().children());\n        column =\n            MltMetadata.structFieldType(\n                List.of(\n                    new MltMetadata.Field(\n                        MltMetadata.scalarFieldType(MltMetadata.ScalarType.STRING, true))));\n      }\n\n      final boolean complex = column.complexType() != null;\n      final boolean logical =\n          (complex && column.complexType().logicalType() != null)\n              || (!complex && column.scalarType().logicalType() != null);\n\n      final var typeCode =\n          MltTypeMap.Tag0x01.encodeColumnType(\n                  (!complex && !logical) ? column.scalarType().physicalType() : null,\n                  (!complex && logical) ? column.scalarType().logicalType() : null,\n                  (complex && !logical) ? column.complexType().physicalType() : null,\n                  (complex && logical) ? column.complexType().logicalType() : null,\n                  column.isNullable(),\n                  complex && !column.complexType().children().isEmpty(),\n                  !complex && column.scalarType().hasLongId())\n              .or(Assertions::fail);\n      assertEquals(typeCode.get(), i);\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/VarintTest.java",
    "content": "package org.maplibre.mlt.converter.encodings;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\nimport java.util.stream.IntStream;\nimport java.util.stream.LongStream;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.decoder.DecodingUtils;\n\npublic class VarintTest {\n  private static final int[] intTestValues = {\n    0,\n    1,\n    0x7F,\n    0x80,\n    0xFE,\n    0xFF,\n    0x100,\n    0xFFF,\n    0x1000,\n    0x4000,\n    0xFFFF,\n    0x10000,\n    0x40000,\n    0xFFFFF,\n    0x100000,\n    0x1FFFFF,\n    0x400000,\n    0xFFFFFFF,\n    0x10000000,\n    0x40000000,\n    0xFFFFFFFF,\n    Integer.MAX_VALUE\n  };\n  private static final long[] longTestValues = {\n    0x100000000L,\n    0x400000000L,\n    0xFFFFFFFFFL,\n    0x1000000000000000L,\n    0x4000000000000000L,\n    Long.MAX_VALUE\n  };\n\n  @Test\n  public void testVarIntEncoding() throws IOException {\n    for (int value : Arrays.stream(intTestValues).flatMap(x -> IntStream.of(x, -x)).toArray()) {\n      final var encoded =\n          EncodingUtils.putVarInt(value, ByteBuffer.wrap(new byte[EncodingUtils.MAX_VARINT_SIZE]))\n              .flip();\n      Assert.equals(EncodingUtils.getVarIntSize(value), encoded.limit());\n      final var decoded1 = DecodingUtils.decodeVarints(encoded.array(), new IntWrapper(0), 1)[0];\n      Assert.equals(value, decoded1);\n      final var decoded2 = DecodingUtils.decodeVarint(new ByteArrayInputStream(encoded.array()));\n      Assert.equals(value, decoded2);\n      final var decoded3 =\n          DecodingUtils.decodeVarintWithLength(new ByteArrayInputStream(encoded.array()));\n      Assert.equals(value, decoded3.getLeft());\n      Assert.equals(encoded.limit(), decoded3.getRight());\n    }\n  }\n\n  @Test\n  public void testVarLongEncoding() throws IOException {\n    final var ints = Arrays.stream(intTestValues).asLongStream().flatMap(x -> LongStream.of(x, -x));\n    final var longs = Arrays.stream(longTestValues).flatMap(x -> LongStream.of(x, -x));\n    for (long value : LongStream.concat(ints, longs).toArray()) {\n      final var encoded =\n          EncodingUtils.putVarInt(value, ByteBuffer.wrap(new byte[EncodingUtils.MAX_VARLONG_SIZE]))\n              .flip();\n      Assert.equals(EncodingUtils.getVarLongSize(value), encoded.limit());\n      final var decoded = DecodingUtils.decodeLongVarint(encoded.array(), new IntWrapper(0));\n      Assert.equals(value, decoded);\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/fsst/FsstTest.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nclass FsstTest {\n  private static final Fsst JAVA = new FsstJava();\n  private static final Fsst JNI = new FsstJni();\n  private static final FsstDebug DEBUG = new FsstDebug();\n\n  @AfterAll\n  static void printStats() {\n    System.err.print(FsstDebug.printStatsOnce());\n  }\n\n  @Test\n  @Disabled\n  void decode_simpleString_ValidEncodedAndDecoded() {\n    test(\"AAAAAAABBBAAACCdddddEEEEEEfffEEEEAAAAAddddCC\");\n  }\n\n  @Test\n  @Disabled\n  void decodeLongRepeated() {\n    test(\"AAAAAAABBBAAACCdddddEEEEEEfffEEEEAAAAAddddCC\".repeat(100));\n  }\n\n  @Test\n  @Disabled\n  void empty() {\n    test(\"\");\n  }\n\n  @Test\n  @Disabled\n  void repeatedStrings() {\n    for (int i = 1; i < 1000; i++) {\n      test(\"a\".repeat(i));\n    }\n  }\n\n  @Test\n  @Disabled\n  void allBytes() {\n    byte[] toEncode = new byte[1000];\n    for (int i = 0; i < toEncode.length; i++) {\n      toEncode[i] = (byte) i;\n    }\n    test(toEncode);\n  }\n\n  static List<Path> tiles() throws IOException {\n    try (var stream = Files.walk(Path.of(\"..\", \"test\"))) {\n      return stream\n          .filter(file -> Files.isRegularFile(file) && file.toString().endsWith(\".mvt\"))\n          .toList();\n    }\n  }\n\n  @ParameterizedTest\n  @MethodSource(\"tiles\")\n  @Disabled\n  void fsstEncodeTile(Path path) throws IOException {\n    // stress-test FSST encoding by using it to encode raw tiles\n    // ideally this would encode just the dictionaries, but it's close enough for now\n    test(Files.readAllBytes(path));\n  }\n\n  private static void test(String input) {\n    test(input.getBytes(StandardCharsets.UTF_8));\n  }\n\n  private static void assertSymbolSortOrder(SymbolTable table) {\n    int last = 2;\n    boolean ones = false;\n    for (int len : table.symbolLengths()) {\n      if (len == 1) {\n        ones = true;\n      }\n      if (ones) {\n        assertEquals(\n            1,\n            len,\n            () ->\n                \"Expected symbols sorted by length with single-byte symbols last, got: \"\n                    + Arrays.toString(table.symbolLengths()));\n      } else {\n        assertTrue(\n            len >= last,\n            () ->\n                \"Expected symbols sorted by length with single-byte symbols last, got: \"\n                    + Arrays.toString(table.symbolLengths()));\n        last = len;\n      }\n    }\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  private static void test(byte[] input) {\n    var encodedJava = JAVA.encode(input);\n    var encodedJni = JNI.encode(input);\n    int maxAllowed = Math.max((int) (encodedJni.weight() * 1.02), encodedJni.weight() + 2);\n    assertTrue(\n        encodedJava.weight() <= maxAllowed,\n        () ->\n            \"\"\"\n            Input: byte[%d]\n            Encoded java >5%% larger than JNI\n            Java: %s\n            JNI: %s\n            \"\"\"\n                .formatted(input.length, encodedJava, encodedJni));\n    assertSymbolSortOrder(encodedJni);\n    assertSymbolSortOrder(encodedJava);\n    assertArrayEquals(input, JAVA.decode(encodedJava));\n    assertArrayEquals(input, JAVA.decode(encodedJni));\n    assertArrayEquals(input, JNI.decode(encodedJava));\n    assertArrayEquals(input, JNI.decode(encodedJni));\n    assertArrayEquals(\n        input,\n        JNI.decode(\n            encodedJava.symbols(), encodedJava.symbolLengths(), encodedJava.compressedData()));\n    DEBUG.encode(input);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/encodings/fsst/SymbolTest.java",
    "content": "package org.maplibre.mlt.converter.encodings.fsst;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.nio.ByteBuffer;\nimport java.util.Arrays;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nclass SymbolTest {\n  Symbol s1 = Symbol.of(1);\n  Symbol s2 = Symbol.of(2);\n  Symbol s3 = Symbol.of(3);\n  Symbol s12 = Symbol.concat(s1, s2);\n  Symbol s12_3 = Symbol.concat(Symbol.concat(s1, s2), s3);\n  Symbol s1_23 = Symbol.concat(s1, Symbol.concat(s2, s3));\n  Symbol s12121212 = Symbol.concat(Symbol.concat(s12, s12), Symbol.concat(s12, s12));\n\n  @Test\n  void testBytes() {\n    assertEquals(1, s1.length());\n    assertEquals(1, s2.length());\n    assertSymbol(s1, 1);\n    assertSymbol(s2, 2);\n    assertEquals(2, s12.length());\n    assertSymbol(s12, 1, 2);\n    assertEquals(3, s12_3.length());\n    assertEquals(3, s1_23.length());\n    assertEquals(s12_3.hashCode(), s1_23.hashCode());\n    assertSymbol(s12_3, 1, 2, 3);\n    assertSymbol(s1_23, 1, 2, 3);\n    assertEquals(8, s12121212.length());\n    assertSymbol(s12121212, 1, 2, 1, 2, 1, 2, 1, 2);\n  }\n\n  @Test\n  void testMatch() {\n    assertTrue(s1.match(ByteBuffer.wrap(new byte[] {1}), 0, 0));\n    assertFalse(s2.match(ByteBuffer.wrap(new byte[] {1}), 0, 0));\n    assertTrue(s2.match(ByteBuffer.wrap(new byte[] {1, 2}), 1, 0));\n    assertFalse(s1.match(ByteBuffer.wrap(new byte[] {1, 2}), 1, 0));\n  }\n\n  @ParameterizedTest\n  @ValueSource(bytes = {1, (byte) 255})\n  void testMatch(byte first) {\n    var matcher = Symbol.concat(Symbol.of(first), Symbol.of(2));\n    byte[] bytesMatching = new byte[] {first, 2, 3, 4, 5, 6, 7, 8, 9, 10};\n    byte[] bytesNotMatching = new byte[] {first, 3, 3, 4, 5, 6, 7, 8, 9, 10};\n    for (int i = 2; i < 10; i++) {\n      ByteBuffer bufMatch = ByteBuffer.wrap(bytesMatching, 0, i);\n      ByteBuffer bufNoMatch = ByteBuffer.wrap(bytesNotMatching, 0, i);\n      assertTrue(matcher.match(bufMatch, 0, 0), \"expected match \" + i);\n      assertFalse(matcher.match(bufNoMatch, 0, 0), \"expected no match \" + i);\n      assertFalse(matcher.match(bufMatch, 1, 0), \"expected match with offset \" + i);\n    }\n  }\n\n  @Test\n  void testCompare() {\n    assertEquals(-1, s1.compareTo(s2));\n    assertEquals(1, s2.compareTo(s1));\n    assertEquals(0, s1.compareTo(Symbol.of(1)));\n\n    assertEquals(-1, s12.compareTo(Symbol.concat(s1, s3)));\n    // longer symbol must compare first\n    assertEquals(-1, s12_3.compareTo(s12));\n  }\n\n  private static void assertSymbol(Symbol s, int... ints) {\n    assertEquals(ints.length, s.length());\n    byte[] bytes = new byte[ints.length];\n    for (int i = 0; i < ints.length; i++) {\n      bytes[i] = (byte) ints[i];\n    }\n    assertEquals(ints[0], s.first());\n    assertEquals(Arrays.hashCode(bytes), s.hashCode());\n    assertArrayEquals(\n        bytes,\n        s.bytes(),\n        \"Expected: %s Actual: %s\".formatted(Arrays.toString(bytes), Arrays.toString(s.bytes())));\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/geometry/HilbertCurveTest.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\npublic class HilbertCurveTest {\n\n  @Test\n  public void level1_hasExpectedStandardOrder() {\n    final var curve = new HilbertCurve(0, 1);\n    assertArrayEquals(new int[] {0, 0}, curve.decode(0));\n    assertArrayEquals(new int[] {0, 1}, curve.decode(1));\n    assertArrayEquals(new int[] {1, 1}, curve.decode(2));\n    assertArrayEquals(new int[] {1, 0}, curve.decode(3));\n  }\n\n  @ParameterizedTest(name = \"level={0}\")\n  @ValueSource(ints = {1, 2, 5, 10, 16})\n  public void encodeDecode_roundTripsForRepresentativeLevels(int level) {\n    assertRoundTripForLevel(level);\n  }\n\n  @Test\n  public void consecutiveIndices_areAxisAdjacentAtLevel6() {\n    final int level = 6;\n    final int maxCoordinate = (1 << level) - 1;\n    final var curve = new HilbertCurve(0, maxCoordinate);\n    final long maxIndexExclusive = 1L << (2L * level);\n    final long step = Math.max(1L, maxIndexExclusive / 4096L);\n    for (long index = 0; index < maxIndexExclusive - 1; index += step) {\n      final int currentIndex = (int) index;\n      final int nextIndex = (int) (index + 1);\n      final var a = curve.decode(currentIndex);\n      final var b = curve.decode(nextIndex);\n      final int manhattan = Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);\n      assertEquals(1, manhattan, \"Hilbert curve should move one grid step per index increment\");\n    }\n  }\n\n  @Test\n  public void throwsWhenLevelIsAboveMaximumAllowed() {\n    assertThrows(IllegalArgumentException.class, () -> assertRoundTripForLevel(17));\n  }\n\n  private static void assertRoundTripForLevel(int level) {\n    final int maxCoordinate = (1 << level) - 1;\n    final var curve = new HilbertCurve(0, maxCoordinate);\n\n    assertRoundTrip(curve, 0, 0);\n    assertRoundTrip(curve, maxCoordinate, 0);\n    assertRoundTrip(curve, 0, maxCoordinate);\n    assertRoundTrip(curve, maxCoordinate, maxCoordinate);\n    assertRoundTrip(curve, maxCoordinate / 2, maxCoordinate / 2);\n\n    final int step = Math.max(1, maxCoordinate / 7);\n    for (int y = 0; y <= maxCoordinate; y += step) {\n      for (int x = 0; x <= maxCoordinate; x += step) {\n        assertRoundTrip(curve, x, y);\n      }\n    }\n  }\n\n  private static void assertRoundTrip(HilbertCurve curve, int x, int y) {\n    final int index = curve.encode(new Vertex(x, y));\n    final int[] decoded = curve.decode(index);\n    assertArrayEquals(\n        new int[] {x, y}, decoded, \"Coordinate should round-trip through encode/decode\");\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/geometry/SpaceFillingCurveTest.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\npublic class SpaceFillingCurveTest {\n\n  @Test\n  @Disabled\n  public void numBits_positiveBounds() {\n    SpaceFillingCurve sfc = new TestSpaceFillingCurve(2, 16);\n    assertEquals(4, sfc.numBits());\n  }\n\n  @Test\n  public void numBits_zeroAndPositiveBounds() {\n    SpaceFillingCurve sfc = new TestSpaceFillingCurve(0, 16);\n    assertEquals(5, sfc.numBits());\n  }\n\n  @Test\n  public void numBits_positiveAndNegativeBounds() {\n    SpaceFillingCurve sfc = new TestSpaceFillingCurve(-16, 16);\n    assertEquals(6, sfc.numBits());\n  }\n\n  private static class TestSpaceFillingCurve extends SpaceFillingCurve {\n    public TestSpaceFillingCurve(int minVertexValue, int maxVertexValue) {\n      super(minVertexValue, maxVertexValue);\n    }\n\n    @Override\n    public int encode(Vertex vertex) {\n      return 0;\n    }\n\n    @Override\n    public int[] decode(int mortonCode) {\n      return new int[0];\n    }\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/geometry/ZOrderCurveTest.java",
    "content": "package org.maplibre.mlt.converter.geometry;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\npublic class ZOrderCurveTest {\n\n  @Test\n  public void level1_hasExpectedStandardOrder() {\n    final var curve = new ZOrderCurve(0, 1);\n    assertArrayEquals(new int[] {0, 0}, curve.decode(0));\n    assertArrayEquals(new int[] {1, 0}, curve.decode(1));\n    assertArrayEquals(new int[] {0, 1}, curve.decode(2));\n    assertArrayEquals(new int[] {1, 1}, curve.decode(3));\n  }\n\n  @ParameterizedTest(name = \"level={0}\")\n  @ValueSource(ints = {1, 2, 5, 10, 16})\n  public void encodeDecode_roundTripsForRepresentativeLevels(int level) {\n    assertRoundTripForLevel(level);\n  }\n\n  @ParameterizedTest(name = \"level={0}\")\n  @ValueSource(ints = {1, 2, 5, 10, 16})\n  public void staticDecode_matchesInstanceDecodeForRepresentativeLevels(int level) {\n    assertStaticDecodeMatchesInstanceDecode(level);\n  }\n\n  @Test\n  public void throwsWhenLevelIsAboveMaximumAllowed() {\n    assertThrows(IllegalArgumentException.class, () -> assertRoundTripForLevel(17));\n  }\n\n  private static void assertRoundTripForLevel(int level) {\n    final int maxCoordinate = (1 << level) - 1;\n    final var curve = new ZOrderCurve(0, maxCoordinate);\n\n    assertRoundTrip(curve, 0, 0);\n    assertRoundTrip(curve, maxCoordinate, 0);\n    assertRoundTrip(curve, 0, maxCoordinate);\n    assertRoundTrip(curve, maxCoordinate, maxCoordinate);\n    assertRoundTrip(curve, maxCoordinate / 2, maxCoordinate / 2);\n\n    final int step = Math.max(1, maxCoordinate / 7);\n    for (int y = 0; y <= maxCoordinate; y += step) {\n      for (int x = 0; x <= maxCoordinate; x += step) {\n        assertRoundTrip(curve, x, y);\n      }\n    }\n  }\n\n  private static void assertStaticDecodeMatchesInstanceDecode(int level) {\n    final int maxCoordinate = (1 << level) - 1;\n    final var curve = new ZOrderCurve(0, maxCoordinate);\n    final long maxIndexExclusive = 1L << (2L * level);\n    final long step = Math.max(1L, maxIndexExclusive / 32L);\n    for (long index = 0; index < maxIndexExclusive; index += step) {\n      final int sampledIndex = (int) index;\n      assertArrayEquals(\n          curve.decode(sampledIndex),\n          ZOrderCurve.decode(sampledIndex, curve.numBits(), curve.coordinateShift()),\n          \"Static decode should match instance decode\");\n    }\n  }\n\n  private static void assertRoundTrip(ZOrderCurve curve, int x, int y) {\n    final int index = curve.encode(new Vertex(x, y));\n    final int[] decoded = curve.decode(index);\n    assertArrayEquals(\n        new int[] {x, y}, decoded, \"Coordinate should round-trip through encode/decode\");\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/converter/tessellation/TessellationUtilsTest.java",
    "content": "package org.maplibre.mlt.converter.tessellation;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.Polygon;\n\npublic class TessellationUtilsTest {\n  private TessellationUtilsTest() {}\n\n  @Test\n  @Disabled\n  public void tessellateMultiPolygon_PolygonsWithoutHoles() {\n    var geometryFactory = new GeometryFactory();\n    var shell1 =\n        new Coordinate[] {\n          new Coordinate(0, 0),\n          new Coordinate(10, 0),\n          new Coordinate(10, 10),\n          new Coordinate(0, 10),\n          new Coordinate(0, 0)\n        };\n    var polygon1 = geometryFactory.createPolygon(shell1);\n    var shell2 =\n        new Coordinate[] {\n          new Coordinate(20, 20),\n          new Coordinate(40, 20),\n          new Coordinate(40, 40),\n          new Coordinate(20, 40),\n          new Coordinate(20, 20)\n        };\n    var polygon2 = geometryFactory.createPolygon(shell2);\n    var multiPolygon = geometryFactory.createMultiPolygon(new Polygon[] {polygon1, polygon2});\n\n    var tessellatedPolygon = TessellationUtils.tessellateMultiPolygon(multiPolygon, null);\n\n    var expectedIndexBuffer =\n        Stream.of(3, 0, 1, 1, 2, 3, 7, 4, 5, 5, 6, 7)\n            .collect(Collectors.toCollection(ArrayList::new));\n\n    assertEquals(4, tessellatedPolygon.numTriangles());\n    assertEquals(expectedIndexBuffer, tessellatedPolygon.indexBuffer());\n  }\n\n  @Test\n  public void tessellateMultiPolygon_PolygonsWithHoles() {\n    var geometryFactory = new GeometryFactory();\n    var shell1 =\n        new Coordinate[] {\n          new Coordinate(0, 0),\n          new Coordinate(10, 0),\n          new Coordinate(10, 10),\n          new Coordinate(0, 10),\n          new Coordinate(0, 0)\n        };\n    var hole1 =\n        new Coordinate[] {\n          new Coordinate(5, 5),\n          new Coordinate(5, 7),\n          new Coordinate(7, 7),\n          new Coordinate(7, 5),\n          new Coordinate(5, 5)\n        };\n    /*var hole2 = new Coordinate[] {\n            new Coordinate(8, 8),\n            new Coordinate(8, 9),\n            new Coordinate(9, 9),\n            new Coordinate(9, 8),\n            new Coordinate(8, 8)\n    };*/\n    var polygon1 =\n        geometryFactory.createPolygon(\n            geometryFactory.createLinearRing(shell1),\n            new LinearRing[] {geometryFactory.createLinearRing(hole1)});\n    var shell2 =\n        new Coordinate[] {\n          new Coordinate(20, 20),\n          new Coordinate(40, 20),\n          new Coordinate(40, 40),\n          new Coordinate(20, 40),\n          new Coordinate(20, 20)\n        };\n    var polygon2 = geometryFactory.createPolygon(shell2);\n    var multiPolygon = geometryFactory.createMultiPolygon(new Polygon[] {polygon1, polygon2});\n\n    var tessellatedPolygon = TessellationUtils.tessellateMultiPolygon(multiPolygon, null);\n\n    assertEquals(10, tessellatedPolygon.numTriangles());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/data/unsigned/UnsignedTest.java",
    "content": "package org.maplibre.mlt.data.unsigned;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nimport java.math.BigInteger;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/** Tests for the {@link Unsigned} interface and its implementations */\nclass UnsignedTest {\n  @Test\n  void testU8OfValidValues() {\n    final var u8Zero = U8.of(0);\n    assertEquals((byte) 0, u8Zero.byteValue());\n    assertEquals(0, u8Zero.intValue());\n    assertEquals(0L, u8Zero.longValue());\n\n    final var u8Max = U8.of(255);\n    assertEquals((byte) 255, u8Max.byteValue());\n    assertEquals(255, u8Max.intValue());\n    assertEquals(255L, u8Max.longValue());\n\n    final var u8Mid = U8.of(128);\n    assertEquals((byte) 128, u8Mid.byteValue());\n    assertEquals(128, u8Mid.intValue());\n    assertEquals(128L, u8Mid.longValue());\n  }\n\n  @Test\n  void testU8OfNegativeValue() {\n    assertThrows(\n        IllegalArgumentException.class, () -> U8.of(-1), \"U8 should reject negative values\");\n  }\n\n  @Test\n  void testU8OfValueTooLarge() {\n    assertThrows(IllegalArgumentException.class, () -> U8.of(256), \"U8 should reject values > 255\");\n  }\n\n  @ParameterizedTest\n  @ValueSource(ints = {0, 1, 127, 128, 254, 255})\n  void testU8ValidRange(int value) {\n    final var u8 = U8.of(value);\n    assertEquals(value, u8.intValue(), \"U8 should preserve unsigned value as int\");\n    assertEquals((long) value, u8.longValue(), \"U8 should preserve unsigned value as long\");\n  }\n\n  @Test\n  void testU8RecordProperties() {\n    assertEquals(42, (int) U8.of(42).value(), \"Record should expose value\");\n  }\n\n  @Test\n  void testU8ToString() {\n    assertEquals(\"u8(0)\", U8.of(0).toString());\n    assertEquals(\"u8(255)\", U8.of(255).toString());\n    assertEquals(\"u8(42)\", U8.of(42).toString());\n  }\n\n  @Test\n  void testU8Equality() {\n    final var u8a = U8.of(42);\n    final var u8b = U8.of(42);\n    final var u8c = U8.of(43);\n\n    assertEquals(u8a, u8b, \"U8 records with same value should be equal\");\n    assertNotEquals(u8a, u8c, \"U8 records with different values should not be equal\");\n  }\n\n  @Test\n  void testU8ImplementsUnsigned() {\n    final var u8 = U8.of(100);\n    assertTrue(u8 instanceof Unsigned, \"U8 should implement Unsigned\");\n    assertNotNull(u8.byteValue());\n    assertNotNull(u8.intValue());\n    assertNotNull(u8.longValue());\n  }\n\n  @Test\n  void testU32OfValidValues() {\n    // Test boundary values\n    final var u32Zero = U32.of(0);\n    assertEquals(0, u32Zero.intValue());\n    assertEquals(0L, u32Zero.longValue());\n\n    final var u32Max = U32.of(0xFFFFFFFFL);\n    assertEquals(-1, u32Max.intValue()); // -1 in signed int representation\n    assertEquals(0xFFFFFFFFL, u32Max.longValue()); // Correct unsigned value\n\n    final var u32Mid = U32.of(0x80000000L);\n    assertEquals(0x80000000L, u32Mid.longValue());\n  }\n\n  @Test\n  void testU32OfNegativeValue() {\n    assertThrows(\n        IllegalArgumentException.class, () -> U32.of(-1), \"U32 should reject negative values\");\n  }\n\n  @Test\n  void testU32OfValueTooLarge() {\n    assertThrows(\n        IllegalArgumentException.class,\n        () -> U32.of(0x100000000L),\n        \"U32 should reject values > 2^32-1\");\n  }\n\n  @ParameterizedTest\n  @ValueSource(longs = {0L, 1L, 0x7FFFFFFFL, 0x80000000L, 0xFFFFFFFEL, 0xFFFFFFFFL})\n  void testU32ValidRange(long value) {\n    assertEquals(value, U32.of(value).longValue(), \"U32 should preserve unsigned value as long\");\n  }\n\n  @Test\n  void testU32RecordProperties() {\n    assertEquals(0x12345678, U32.of(0x12345678L).value(), \"Record should expose value\");\n  }\n\n  @Test\n  void testU32ToString() {\n    assertEquals(\"u32(0)\", U32.of(0).toString());\n    assertEquals(\"u32(4294967295)\", U32.of(0xFFFFFFFFL).toString());\n    assertEquals(\"u32(305419896)\", U32.of(0x12345678L).toString());\n  }\n\n  @Test\n  void testU32Equality() {\n    final var u32a = U32.of(0x12345678L);\n    final var u32b = U32.of(0x12345678L);\n    final var u32c = U32.of(0x12345679L);\n\n    assertEquals(u32a, u32b, \"U32 records with same value should be equal\");\n    assertNotEquals(u32a, u32c, \"U32 records with different values should not be equal\");\n  }\n\n  @Test\n  void testU32ImplementsUnsigned() {\n    final var u32 = U32.of(1000000L);\n    assertTrue(u32 instanceof Unsigned, \"U32 should implement Unsigned\");\n    assertNotNull(u32.intValue());\n    assertNotNull(u32.longValue());\n  }\n\n  @Test\n  void testU32ByteValueReturnsNullWhenOutOfRange() {\n    final var u32InRange = U32.of(100);\n    assertNotNull(u32InRange.byteValue(), \"U32 with value in byte range should return non-null\");\n\n    final var u32OutOfRange = U32.of(256);\n    assertNull(u32OutOfRange.byteValue(), \"U32 with value > 255 should return null for byteValue\");\n  }\n\n  @Test\n  void testU32ByteValueBoundary() {\n    final var u32At127 = U32.of(127);\n    assertNotNull(u32At127.byteValue(), \"U32 with value 127 should return non-null\");\n\n    final var u32At128 = U32.of(128);\n    assertNull(u32At128.byteValue(), \"U32 with value 128 should return null (negative as byte)\");\n  }\n\n  @Test\n  void testU64OfValidValues() {\n    // Test boundary values\n    final var u64Zero = U64.of(BigInteger.ZERO);\n    assertEquals(0L, u64Zero.longValue());\n    assertEquals(0, u64Zero.intValue());\n\n    final var u64Max = U64.of(new BigInteger(\"18446744073709551615\")); // 2^64 - 1\n    assertEquals(-1L, u64Max.longValue()); // -1 in signed long representation\n  }\n\n  @Test\n  void testU64OfNegativeValue() {\n    assertThrows(\n        IllegalArgumentException.class,\n        () -> U64.of(BigInteger.valueOf(-1)),\n        \"U64 should reject negative values\");\n  }\n\n  @Test\n  void testU64OfValueTooLarge() {\n    final var tooLarge = new BigInteger(\"18446744073709551616\"); // 2^64\n    assertThrows(\n        IllegalArgumentException.class, () -> U64.of(tooLarge), \"U64 should reject values >= 2^64\");\n  }\n\n  @ParameterizedTest\n  @ValueSource(longs = {0L, 1L, Long.MAX_VALUE, -1L})\n  void testU64ValidRange(long value) {\n    final var u64 = U64.of(BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL));\n    assertNotNull(u64.longValue(), \"U64 should accept valid unsigned values\");\n  }\n\n  @Test\n  void testU64RecordProperties() {\n    final var u64 = U64.of(BigInteger.valueOf(0x123456789ABCDEFL));\n    assertEquals(0x123456789ABCDEFL, u64.value(), \"Record should expose value\");\n  }\n\n  @Test\n  void testU64ToString() {\n    assertEquals(\"u64(0)\", U64.of(BigInteger.ZERO).toString());\n    final var u64Max = U64.of(new BigInteger(\"18446744073709551615\"));\n    assertEquals(\"u64(18446744073709551615)\", u64Max.toString());\n  }\n\n  @Test\n  void testU64Equality() {\n    final var u64a = U64.of(BigInteger.valueOf(0x123456789ABCDEFL));\n    final var u64b = U64.of(BigInteger.valueOf(0x123456789ABCDEFL));\n    final var u64c = U64.of(BigInteger.valueOf(0x123456789ABCDF0L));\n\n    assertEquals(u64a, u64b, \"U64 records with same value should be equal\");\n    assertNotEquals(u64a, u64c, \"U64 records with different values should not be equal\");\n  }\n\n  @Test\n  void testU64ImplementsUnsigned() {\n    final var u64 = U64.of(BigInteger.valueOf(1000000L));\n    assertTrue(u64 instanceof Unsigned, \"U64 should implement Unsigned\");\n    assertNotNull(u64.longValue());\n  }\n\n  @Test\n  void testU64ByteValueReturnsNullWhenOutOfRange() {\n    final var u64InRange = U64.of(BigInteger.valueOf(100));\n    assertNotNull(u64InRange.byteValue(), \"U64 with value in byte range should return non-null\");\n\n    final var u64OutOfRange = U64.of(BigInteger.valueOf(256));\n    assertNull(u64OutOfRange.byteValue(), \"U64 with value > 255 should return null for byteValue\");\n  }\n\n  @Test\n  void testU64IntValueReturnsNullWhenOutOfRange() {\n    final var u64InRange = U64.of(BigInteger.valueOf(0x12345678L));\n    assertNotNull(u64InRange.intValue(), \"U64 with value in int range should return non-null\");\n\n    final var u64OutOfRange = U64.of(BigInteger.valueOf(0x100000000L));\n    assertNull(u64OutOfRange.intValue(), \"U64 with value > 2^31-1 should return null for intValue\");\n  }\n\n  @Test\n  void testU64ByteAndIntValueBoundaries() {\n    // Test byte boundary - byteValue returns non-null only for 0-127\n    final var u64At127 = U64.of(BigInteger.valueOf(127));\n    assertNotNull(u64At127.byteValue(), \"U64 with value 127 should return non-null for byteValue\");\n\n    final var u64At128 = U64.of(BigInteger.valueOf(128));\n    assertNull(\n        u64At128.byteValue(),\n        \"U64 with value 128 should return null for byteValue (negative as byte)\");\n\n    // Test int boundary\n    final var u64AtMaxInt = U64.of(BigInteger.valueOf(0x7FFFFFFFL));\n    assertNotNull(\n        u64AtMaxInt.intValue(), \"U64 with value 2^31-1 should return non-null for intValue\");\n\n    final var u64AtMaxIntPlus1 = U64.of(BigInteger.valueOf(0x80000000L));\n    assertNull(\n        u64AtMaxIntPlus1.intValue(),\n        \"U64 with value 2^31 should return null for intValue (negative as int)\");\n  }\n\n  @Test\n  void testConversionBetweenTypes() {\n    final var u8 = U8.of(42);\n    final var u32 = U32.of(42L);\n    final var u64 = U64.of(BigInteger.valueOf(42));\n\n    assertEquals(u8.intValue(), u32.intValue());\n    assertEquals(u32.longValue(), u64.longValue());\n    assertEquals(u8.longValue(), u64.longValue());\n  }\n\n  @Test\n  void testU8WithHighByteValue() {\n    // Test that U8 properly handles value 255 as unsigned\n    final var u8 = U8.of(255);\n    assertEquals((byte) -1, u8.byteValue()); // Signed byte representation\n    assertEquals(255, u8.intValue()); // Unsigned int value\n    assertEquals(255L, u8.longValue()); // Unsigned long value\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/ByteRleTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.converter.encodings.ByteRleEncoder;\n\npublic class ByteRleTest {\n\n  @Test\n  public void testEncodeDecodeRun() throws IOException {\n    // Test encoding a run of identical bytes\n    byte[] values = new byte[] {1, 1, 1, 1, 1, 1, 1};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    // Decode\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    byte[] decoded = new byte[values.length];\n    for (int i = 0; i < values.length; i++) {\n      decoded[i] = decoder.next();\n    }\n\n    assertArrayEquals(values, decoded);\n  }\n\n  @Test\n  public void testEncodeDecodeLiterals() throws IOException {\n    // Test encoding literal bytes (no runs)\n    byte[] values = new byte[] {1, 2, 3, 4, 5, 6, 7};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    // Decode\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    byte[] decoded = new byte[values.length];\n    for (int i = 0; i < values.length; i++) {\n      decoded[i] = decoder.next();\n    }\n\n    assertArrayEquals(values, decoded);\n  }\n\n  @Test\n  public void testEncodeDecodeMixed() throws IOException {\n    // Test encoding a mix of runs and literals\n    byte[] values = new byte[] {1, 1, 1, 2, 3, 4, 5, 5, 5, 5};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    // Decode\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    byte[] decoded = new byte[values.length];\n    for (int i = 0; i < values.length; i++) {\n      decoded[i] = decoder.next();\n    }\n\n    assertArrayEquals(values, decoded);\n  }\n\n  @Test\n  public void testEncodeDecodeLongRun() throws IOException {\n    // Test encoding a long run (exceeding minimum repeat size)\n    byte[] values = new byte[150];\n    for (int i = 0; i < values.length; i++) {\n      values[i] = 42;\n    }\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n    // Should be compressed significantly\n    assertTrue(encoded.length < values.length);\n\n    // Decode\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    byte[] decoded = new byte[values.length];\n    for (int i = 0; i < values.length; i++) {\n      decoded[i] = decoder.next();\n    }\n\n    assertArrayEquals(values, decoded);\n  }\n\n  @Test\n  public void testEncodeSingleByte() throws IOException {\n    byte[] values = new byte[] {7};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    // Decode\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    assertEquals(7, decoder.next());\n  }\n\n  @Test\n  public void testEncodeEmpty() throws IOException {\n    byte[] values = new byte[0];\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n    assertEquals(0, encoded.length);\n  }\n\n  @Test\n  public void testEncoderFlush() throws IOException {\n    ByteRleEncoder encoder = new ByteRleEncoder();\n    encoder.write((byte) 1);\n    encoder.write((byte) 2);\n    encoder.write((byte) 3);\n\n    byte[] encoded = encoder.toByteArray();\n    assertNotNull(encoded);\n\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    assertEquals(1, decoder.next());\n    assertEquals(2, decoder.next());\n    assertEquals(3, decoder.next());\n  }\n\n  @Test\n  public void testCompatibilityWithDecodingUtils() throws IOException {\n    // Test that our implementation works with the existing DecodingUtils\n    byte[] values = new byte[] {5, 5, 5, 5, 5};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n\n    IntWrapper pos = new IntWrapper(0);\n    byte[] decoded = DecodingUtils.decodeByteRle(encoded, values.length, encoded.length, pos);\n\n    assertArrayEquals(values, decoded);\n    assertEquals(encoded.length, pos.get());\n  }\n\n  @Test\n  public void testMinimalRun() throws IOException {\n    // Test the minimum run size (3 bytes)\n    byte[] values = new byte[] {7, 7, 7};\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    assertEquals(7, decoder.next());\n    assertEquals(7, decoder.next());\n    assertEquals(7, decoder.next());\n  }\n\n  @Test\n  public void testMaxLiteralSize() throws IOException {\n    // Test encoding maximum literal size (128 bytes)\n    byte[] values = new byte[128];\n    for (int i = 0; i < values.length; i++) {\n      values[i] = (byte) (i % 256);\n    }\n\n    byte[] encoded = ByteRleEncoder.encode(values);\n    assertNotNull(encoded);\n\n    ByteRleDecoder decoder = new ByteRleDecoder(encoded, 0, encoded.length);\n    byte[] decoded = new byte[values.length];\n    for (int i = 0; i < values.length; i++) {\n      decoded[i] = decoder.next();\n    }\n\n    assertArrayEquals(values, decoded);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/DecodingUtilsTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\n\nimport java.io.IOException;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.decoder.vectorized.VectorizedDecodingUtils;\n\npublic class DecodingUtilsTest {\n\n  @Test\n  public void decodeLongVarints() throws IOException {\n    var value = (long) Math.pow(2, 54);\n    var value2 = (long) Math.pow(2, 17);\n    var value3 = (long) Math.pow(2, 57);\n    var encodedValues =\n        EncodingUtils.encodeVarints(new long[] {value, value2, value3}, false, false);\n\n    final var pos = new IntWrapper(0);\n    final var decodedValue = DecodingUtils.decodeLongVarints(encodedValues, pos, 3);\n\n    Assert.equals(value, decodedValue[0]);\n    Assert.equals(value2, decodedValue[1]);\n    Assert.equals(value3, decodedValue[2]);\n\n    Assert.equals(encodedValues.length, pos.get());\n  }\n\n  @Test\n  public void decodeZigZag_LongValue() {\n    var value = (long) Math.pow(2, 54);\n    var value2 = (long) -Math.pow(2, 44);\n    var encodedValue = EncodingUtils.encodeZigZag(value);\n    var encodedValue2 = EncodingUtils.encodeZigZag(value2);\n\n    var decodedValue = DecodingUtils.decodeZigZag(encodedValue);\n    var decodedValue2 = DecodingUtils.decodeZigZag(encodedValue2);\n\n    Assert.equals(value, decodedValue);\n    Assert.equals(value2, decodedValue2);\n  }\n\n  @Test\n  void decodeFastPfor() {\n    var values = new int[] {5, 10, 15, 20, 25, 30, 35, 40};\n    var encoded = EncodingUtils.encodeFastPfor128(values, false, false);\n    var pos = new IntWrapper(0);\n    var decoded =\n        VectorizedDecodingUtils.decodeFastPfor(encoded, values.length, encoded.length, pos);\n\n    assertArrayEquals(values, decoded.array());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/DoubleDecoderTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.util.List;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.converter.encodings.DoubleEncoder;\nimport org.maplibre.mlt.converter.encodings.FloatEncoder;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\n\npublic class DoubleDecoderTest {\n\n  @Test\n  public void decodeDoubleStream_DoubleEncodedValues_ReturnsExactValues() throws IOException {\n    final var encoded = ByteArrayUtil.concat(DoubleEncoder.encodeDoubleStream(f64Values));\n    final var offset = new IntWrapper(0);\n    final var streamMetadata = StreamMetadata.decode(encoded, offset);\n    final var decoded = DoubleDecoder.decodeDoubleStream(encoded, offset, streamMetadata);\n    Assert.equals(f64Values, decoded);\n  }\n\n  @Test\n  public void decodeDoubleStream_FloatEncodedValues_ReturnsConvertedDoubleValues()\n      throws IOException {\n    final var f32Values = f64Values.stream().map(Double::floatValue).toList();\n    final var encoded = ByteArrayUtil.concat(FloatEncoder.encodeFloatStream(f32Values));\n    final var offset = new IntWrapper(0);\n    final var streamMetadata = StreamMetadata.decode(encoded, offset);\n    final var decoded = DoubleDecoder.decodeDoubleStream(encoded, offset, streamMetadata);\n    Assert.equals(f32Values.stream().map(Double::valueOf).toList(), decoded);\n  }\n\n  @Test\n  public void decodeDoubleStream_EmptyStream_ReturnsEmptyList() throws IOException {\n    final var values = List.<Double>of();\n    final var encoded = ByteArrayUtil.concat(DoubleEncoder.encodeDoubleStream(values));\n    final var offset = new IntWrapper(0);\n    final var streamMetadata = StreamMetadata.decode(encoded, offset);\n    final var decoded = DoubleDecoder.decodeDoubleStream(encoded, offset, streamMetadata);\n    Assert.equals(values, decoded);\n  }\n\n  final List<Double> f64Values =\n      List.of(\n          1.25,\n          -3.5,\n          6.02214076e23,\n          -0.0,\n          123456.789,\n          Double.POSITIVE_INFINITY,\n          Double.NEGATIVE_INFINITY,\n          Double.NaN);\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/IntegerDecoderTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport me.lemire.integercompression.IntWrapper;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.converter.CollectionUtils;\nimport org.maplibre.mlt.converter.encodings.EncodingUtils;\nimport org.maplibre.mlt.converter.encodings.IntegerEncoder;\nimport org.maplibre.mlt.metadata.stream.LogicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.LogicalStreamType;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.stream.PhysicalStreamType;\nimport org.maplibre.mlt.metadata.stream.StreamMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\n\npublic class IntegerDecoderTest {\n  @Test\n  public void encode_Int_Limits() throws IOException {\n    for (int v : new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE}) {\n      final var encoded = EncodingUtils.encodeVarint(v, false);\n      final var decoded = DecodingUtils.decodeVarints(encoded, new IntWrapper(0), 1)[0];\n      Assert.equals(decoded, v);\n    }\n  }\n\n  @Test\n  public void encode_Int_Limits_ZigZag() throws IOException {\n    for (int v : new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE}) {\n      final var encoded = EncodingUtils.encodeVarint(v, true);\n      final var zigzag = DecodingUtils.decodeVarints(encoded, new IntWrapper(0), 1)[0];\n      final var decoded = DecodingUtils.decodeZigZag(zigzag);\n      Assert.equals(decoded, v);\n    }\n  }\n\n  private static ArrayList<byte[]> encodeIntStream(\n      List<Integer> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      @SuppressWarnings(\"SameParameterValue\") boolean isSigned,\n      @SuppressWarnings(\"SameParameterValue\") PhysicalStreamType streamType,\n      @SuppressWarnings(\"SameParameterValue\") LogicalStreamType logicalStreamType)\n      throws IOException {\n    return IntegerEncoder.encodeIntStream(\n        values, physicalLevelTechnique, isSigned, streamType, logicalStreamType);\n  }\n\n  @Test\n  public void decodeIntStream_SignedIntegerValues_PlainFastPforEncode() throws IOException {\n    var values = List.of(1, 2, 7, 3, -4, 5, 1, -8);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.FAST_PFOR, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.NONE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  @Test\n  public void decodeIntStream_SignedIntegerValues_PlainVarintEncode() throws IOException {\n    var values = List.of(1, 2, 7, 3, -4, 5, 1, -8);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.VARINT, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.NONE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  @Test\n  @Disabled\n  public void decodeIntStream_SignedIntegerValues_FastPforDeltaRleEncode() throws IOException {\n    var values = List.of(-1, -2, -3, -4, -5, -6, -7, 8);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.FAST_PFOR, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n  }\n\n  @Test\n  @Disabled\n  public void decodeIntStream_SignedIntegerValues_VarintDeltaRleEncode() throws IOException {\n    var values = List.of(-1, -2, -3, -4, -5, -6, -7, 8);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.VARINT, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n  }\n\n  @Test\n  @Disabled\n  public void decodeIntStream_SignedIntegerValues_FastPforRleEncode() throws IOException {\n    var values = List.of(-1, -1, -1, -1, -1, -1, -2, -2);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.FAST_PFOR, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.RLE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  @Test\n  @Disabled\n  public void decodeIntStream_SignedIntegerValues_VarintRleEncode() throws IOException {\n    var values = List.of(-1, -1, -1, -1, -1, -1, -2, -2);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.VARINT, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.RLE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  @Test\n  @Disabled\n  public void decodeIntStream_UnsignedIntegerValues_VarintRleEncode() throws IOException {\n    var values = List.of(1, 1, 1, 1, 1, 1, 2, 2);\n    var encodedStream =\n        ByteArrayUtil.concat(\n            encodeIntStream(\n                values, PhysicalLevelTechnique.VARINT, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues = IntegerDecoder.decodeIntStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.RLE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  private static ArrayList<byte[]> encodeLongStream(\n      List<Long> values,\n      @SuppressWarnings(\"SameParameterValue\") boolean isSigned,\n      @SuppressWarnings(\"SameParameterValue\") PhysicalStreamType streamType,\n      @SuppressWarnings(\"SameParameterValue\") LogicalStreamType logicalStreamType)\n      throws IOException {\n    return IntegerEncoder.encodeLongStream(\n        CollectionUtils.unboxLongs(values), isSigned, streamType, logicalStreamType);\n  }\n\n  @Test\n  @Disabled\n  public void decodeLongStream_SignedIntegerValues_PlainEncode() throws IOException {\n    final var values = List.of(1L, 2L, 7L, 3L, -4L, 5L, 1L, -8L);\n    var encodedStream =\n        ByteArrayUtil.concat(encodeLongStream(values, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues =\n        IntegerDecoder.decodeLongStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.NONE, streamMetadata.logicalLevelTechnique1());\n  }\n\n  @Test\n  @Disabled\n  public void decodeLongStream_SignedIntegerValues_DeltaRleEncode() throws IOException {\n    final var values = List.of(-1L, -2L, -3L, -4L, -5L, -6L, -7L, 8L);\n    var encodedStream =\n        ByteArrayUtil.concat(encodeLongStream(values, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues =\n        IntegerDecoder.decodeLongStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n  }\n\n  @Test\n  @Disabled\n  public void decodeLongStream_SignedIntegerValues_RleEncode() throws IOException {\n    final var values = List.of(-1L, -1L, -1L, -1L, -1L, -1L, -2L, -2L);\n    var encodedStream =\n        ByteArrayUtil.concat(encodeLongStream(values, true, PhysicalStreamType.DATA, null));\n\n    var offset = new IntWrapper(0);\n    var streamMetadata = StreamMetadata.decode(encodedStream, offset);\n    var decodedValues =\n        IntegerDecoder.decodeLongStream(encodedStream, offset, streamMetadata, true);\n\n    Assert.equals(values, decodedValues);\n    Assert.equals(LogicalLevelTechnique.RLE, streamMetadata.logicalLevelTechnique1());\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/MltDecoderTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport org.apache.commons.lang3.tuple.Triple;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.TestUtils;\nimport org.maplibre.mlt.compare.CompareHelper;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\n\n@FunctionalInterface\ninterface TriConsumer<A, B, C> {\n  void apply(A a, B b, C c) throws IOException;\n}\n\npublic class MltDecoderTest {\n\n  private static Stream<Triple<Integer, Integer, Integer>> bingMapsTileIdProvider() {\n    return Stream.of(\n        Triple.of(4, 8, 5), Triple.of(5, 16, 11), Triple.of(6, 32, 22), Triple.of(7, 65, 42));\n  }\n\n  private static Stream<Triple<Integer, Integer, Integer>> omtTileIdProvider() {\n    return Stream.of(\n        Triple.of(0, 0, 0),\n        Triple.of(1, 1, 1),\n        // Triple.of(2, 2, 2),   // TODO: fix -> 2_2_2\n        Triple.of(3, 4, 5),\n        Triple.of(4, 8, 10),\n        Triple.of(5, 16, 21),\n        Triple.of(6, 32, 41),\n        Triple.of(7, 66, 84),\n        Triple.of(8, 134, 171),\n        Triple.of(9, 265, 341),\n        Triple.of(10, 532, 682),\n        Triple.of(11, 1064, 1367),\n        Triple.of(12, 2132, 2734),\n        Triple.of(13, 4265, 5467),\n        Triple.of(14, 8298, 10748));\n  }\n\n  /* Decode tiles in an in-memory format optimized for sequential access */\n\n  @DisplayName(\"Decode scalar unsorted OpenMapTiles schema based vector tiles\")\n  @ParameterizedTest\n  @MethodSource(\"omtTileIdProvider\")\n  public void decodeMlTile_UnsortedOMT(Triple<Integer, Integer, Integer> tileId)\n      throws IOException, URISyntaxException {\n    final var id =\n        String.format(\"%s_%s_%s\", tileId.getLeft(), tileId.getMiddle(), tileId.getRight());\n    final var useFastPFOR = (tileId.getLeft() & 1) != 0;\n    final var useFSST = (tileId.getMiddle() & 1) != 0;\n    // TODO: Why doesn't tessellation work for these\n    final var tessellate = false; // (tileId.getRight() & 1) != 0;\n    testTileSequential(id, TestSettings.OMT_MVT_PATH, useFastPFOR, useFSST, tessellate);\n  }\n\n  private void testTileSequential(\n      String tileId, String tileDirectory, boolean useFastPFOR, boolean useFSST, boolean tessellate)\n      throws IOException, URISyntaxException {\n    testTile(\n        tileId,\n        tileDirectory,\n        (mlTile, tileMetadata, mvTile) -> {\n          final var decodedTile = MltDecoder.decodeMlTile(mlTile);\n          Assertions.assertNotNull(decodedTile);\n\n          final var compareResult =\n              CompareHelper.compareTiles(decodedTile, mvTile, CompareHelper.CompareMode.All);\n          Assertions.assertFalse(compareResult.isPresent());\n        },\n        TestUtils.Optimization.NONE,\n        List.of(),\n        true,\n        true,\n        tessellate);\n  }\n\n  private void testTile(\n      String tileId,\n      String tileDirectory,\n      TriConsumer<byte[], MltMetadata.TileSetMetadata, MapboxVectorTile> decodeAndCompare,\n      @SuppressWarnings(\"SameParameterValue\") TestUtils.Optimization optimization,\n      List<String> reassignableLayers,\n      @SuppressWarnings(\"SameParameterValue\") boolean useFastPFOR,\n      @SuppressWarnings(\"SameParameterValue\") boolean useFSST,\n      @SuppressWarnings(\"SameParameterValue\") boolean tessellate)\n      throws IOException, URISyntaxException {\n    var mvtFilePath = Paths.get(tileDirectory, tileId + \".mvt\");\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var columnMapping = new ColumnMapping(\"name\", \":\", true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(columnMapping));\n    final var isIdPresent = true;\n    final var tileMetadata =\n        MltConverter.createTilesetMetadata(mvTile, columnMappings, isIdPresent);\n\n    var allowSorting = optimization == TestUtils.Optimization.SORTED;\n    var featureTableOptimization =\n        new FeatureTableOptimizations(allowSorting, false, List.of(columnMapping));\n    var optimizations =\n        TestSettings.OPTIMIZED_MVT_LAYERS.stream()\n            .collect(Collectors.toMap(l -> l, l -> featureTableOptimization));\n\n    /* Only regenerate the ids for specific layers when the column is not sorted for comparison reasons */\n    if (optimization == TestUtils.Optimization.IDS_REASSIGNED) {\n      for (var reassignableLayer : reassignableLayers) {\n        optimizations.put(\n            reassignableLayer, new FeatureTableOptimizations(false, true, List.of(columnMapping)));\n      }\n    }\n\n    final var config =\n        ConversionConfig.builder()\n            .includeIds(true)\n            .useFastPFOR(useFastPFOR)\n            .useFSST(useFSST)\n            .optimizations(optimizations)\n            .preTessellatePolygons(tessellate)\n            .build();\n\n    final var mlTile = MltConverter.encode(mvTile, tileMetadata, config, null);\n    Assertions.assertNotNull(mlTile);\n\n    decodeAndCompare.apply(mlTile, tileMetadata, mvTile);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/MltDecoderTest2.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\nimport org.apache.commons.lang3.NotImplementedException;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.TestUtils;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\n\nenum DecoderType {\n  BOTH,\n  SEQUENTIAL,\n  VECTORIZED\n}\n\nenum EncodingType {\n  BOTH,\n  NONADVANCED,\n  ADVANCED\n}\n\nrecord DecodingResult(int numErrors, int numErrorsAdvanced) {}\n\npublic class MltDecoderTest2 {\n\n  /* Bing Maps tests --------------------------------------------------------- */\n\n  // TODO: after https://github.com/maplibre/maplibre-tile-spec/issues/186 is fixed\n  // remove this test and start testing all the Bing tiles with sorting enabled\n  @Test\n  @Disabled\n  public void decodeBingTilesSortedFail() throws IOException {\n    var tileId = \"4-8-5\";\n    var result =\n        testTile(tileId, TestSettings.BING_MVT_PATH, DecoderType.BOTH, EncodingType.ADVANCED, true);\n    assertEquals(\n        1148,\n        result.numErrorsAdvanced(),\n        \"Error for \" + tileId + \"/advanced: \" + result.numErrorsAdvanced());\n  }\n\n  private static Stream<String> bingProvider() {\n    return Stream.of(\n        \"4-8-5\", \"4-9-5\", \"4-12-6\", \"4-13-6\", \"5-16-11\", \"5-17-11\", \"5-17-10\", \"6-32-22\", \"6-33-22\",\n        \"6-32-23\", \"6-32-21\", \"7-65-42\", \"7-66-42\", \"7-66-43\", \"7-66-44\");\n  }\n\n  @DisplayName(\"Decode Bing Tiles\")\n  @ParameterizedTest\n  @MethodSource(\"bingProvider\")\n  @Disabled\n  public void decodeBingTiles(String tileId) throws IOException {\n    var result =\n        testTile(tileId, TestSettings.BING_MVT_PATH, DecoderType.BOTH, EncodingType.BOTH, false);\n    assertEquals(\n        0, result.numErrors(), \"Error for \" + tileId + \"/non-advanced: \" + result.numErrors());\n    assertEquals(\n        0,\n        result.numErrorsAdvanced(),\n        \"Error for \" + tileId + \"/advanced: \" + result.numErrorsAdvanced());\n  }\n\n  // TODO:\n  // once https://github.com/maplibre/maplibre-tile-spec/issues/182 is fixed\n  // add the \"5-16-9\" tile to the bingProvider\n  // and remove this test\n  @Disabled\n  @Test\n  public void currentlyFailingBingDecoding1() {\n    var exception =\n        assertThrows(\n            Exception.class,\n            () ->\n                testTile(\n                    \"5-16-9\",\n                    TestSettings.BING_MVT_PATH,\n                    DecoderType.VECTORIZED,\n                    EncodingType.ADVANCED,\n                    false));\n    assertEquals(\n        \"java.lang.IllegalArgumentException: VectorType not supported yet: CONST\",\n        exception.toString());\n  }\n\n  /* OpenMapTiles schema based vector tiles tests  --------------------------------------------------------- */\n\n  // TODO: after https://github.com/maplibre/maplibre-tile-spec/issues/185 is fixed\n  // remove this test and start testing all the OMT tiles with sorting enabled\n  @Test\n  @Disabled\n  public void decodeOMTTilesSortedFail() {\n    var exception =\n        assertThrows(\n            Exception.class,\n            () ->\n                testTile(\n                    \"4_8_10\",\n                    TestSettings.OMT_MVT_PATH,\n                    DecoderType.BOTH,\n                    EncodingType.ADVANCED,\n                    true));\n    assertEquals(\"java.lang.IndexOutOfBoundsException\", exception.toString());\n  }\n\n  private static Stream<String> omtProvider() {\n    return Stream.of(\n        \"2_2_2\",\n        \"3_4_5\",\n        \"4_8_10\",\n        \"4_3_9\",\n        \"5_16_21\",\n        \"5_16_20\",\n        \"6_32_41\",\n        \"6_33_42\",\n        \"7_66_84\",\n        \"7_66_85\",\n        \"8_134_171\",\n        \"8_132_170\",\n        \"9_265_341\",\n        \"10_532_682\",\n        \"11_1064_1367\",\n        \"12_2132_2734\",\n        \"13_4265_5467\",\n        \"14_8298_10748\",\n        \"14_8299_10748\");\n  }\n\n  @DisplayName(\"Decode OMT Tiles (advanced encodings, non-sorted)\")\n  @ParameterizedTest()\n  @MethodSource(\"omtProvider\")\n  @Disabled\n  public void decodeOMTTiles(String tileId) throws IOException {\n    var result =\n        testTile(tileId, TestSettings.OMT_MVT_PATH, DecoderType.BOTH, EncodingType.ADVANCED, false);\n    assertEquals(\n        0,\n        result.numErrorsAdvanced(),\n        \"Error for \" + tileId + \"/advanced: \" + result.numErrorsAdvanced());\n  }\n\n  @DisplayName(\"Decode OMT Tiles (non-advanced encodings)\")\n  @ParameterizedTest()\n  @MethodSource(\"omtProvider\")\n  @Disabled\n  public void decodeOMTTiles2(String tileId) throws IOException {\n    if (Objects.equals(tileId, \"13_4265_5467\")\n        || Objects.equals(tileId, \"14_8298_10748\")\n        || Objects.equals(tileId, \"14_8299_10748\")) {\n      // TODO remove this special case for these 3 tiles once this bug is fixed:\n      // https://github.com/maplibre/maplibre-tile-spec/issues/183\n      var exception =\n          assertThrows(\n              Exception.class,\n              () ->\n                  testTile(\n                      tileId,\n                      TestSettings.OMT_MVT_PATH,\n                      DecoderType.BOTH,\n                      EncodingType.NONADVANCED,\n                      false));\n      assertTrue(exception.toString().contains(\"ArrayIndexOutOfBoundsException\"));\n    } else {\n      var result =\n          testTile(\n              tileId, TestSettings.OMT_MVT_PATH, DecoderType.BOTH, EncodingType.NONADVANCED, false);\n      assertEquals(\n          0, result.numErrors(), \"Error for \" + tileId + \"/advanced: \" + result.numErrors());\n    }\n  }\n\n  /* Test utility functions */\n\n  private DecodingResult testTile(\n      String tileId,\n      String tileDirectory,\n      DecoderType decoder,\n      EncodingType encoding,\n      boolean allowSorting)\n      throws IOException {\n    var mvtFilePath = Paths.get(tileDirectory, tileId + \".mvt\");\n    var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var columnMappings = new ColumnMappingConfig();\n    var tileMetadata =\n        MltConverter.createTilesetMetadata(\n            mvTile, columnMappings, true, ConversionConfig.TypeMismatchPolicy.COERCE);\n\n    var allowIdRegeneration = false;\n    var optimization = new FeatureTableOptimizations(allowSorting, allowIdRegeneration, List.of());\n    var optimizations =\n        TestSettings.OPTIMIZED_MVT_LAYERS.stream()\n            .collect(Collectors.toMap(l -> l, l -> optimization));\n    var includeIds = true;\n    var mlTile =\n        MltConverter.encode(\n            mvTile,\n            tileMetadata,\n            ConversionConfig.builder()\n                .includeIds(includeIds)\n                .useFastPFOR(false)\n                .useFSST(false)\n                .optimizations(optimizations)\n                .build(),\n            null);\n    var mlTileAdvanced =\n        MltConverter.encode(\n            mvTile,\n            tileMetadata,\n            ConversionConfig.builder()\n                .includeIds(includeIds)\n                .useFastPFOR(true)\n                .useFSST(true)\n                .optimizations(optimizations)\n                .build(),\n            null);\n    int numErrors = -1;\n    int numErrorsAdvanced = -1;\n    if (decoder == DecoderType.SEQUENTIAL || decoder == DecoderType.BOTH) {\n      if (encoding == EncodingType.ADVANCED || encoding == EncodingType.BOTH) {\n        var decodedAdvanced = MltDecoder.decodeMlTile(mlTileAdvanced);\n        numErrorsAdvanced +=\n            TestUtils.compareTilesSequential(decodedAdvanced, mvTile, allowSorting);\n      }\n      if (encoding == EncodingType.NONADVANCED || encoding == EncodingType.BOTH) {\n        var decoded = MltDecoder.decodeMlTile(mlTile);\n        numErrors += TestUtils.compareTilesSequential(decoded, mvTile, allowSorting);\n      }\n    }\n    if (decoder == DecoderType.VECTORIZED || decoder == DecoderType.BOTH) {\n      throw new NotImplementedException(\"Vectorized decoding is not available\");\n    }\n    return new DecodingResult(numErrors, numErrorsAdvanced);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/decoder/StringDecoderTest.java",
    "content": "package org.maplibre.mlt.decoder;\n\nimport java.io.IOException;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.regex.Pattern;\nimport me.lemire.integercompression.IntWrapper;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.locationtech.jts.util.Assert;\nimport org.maplibre.mlt.TestSettings;\nimport org.maplibre.mlt.TestUtils;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.converter.encodings.StringEncoder;\nimport org.maplibre.mlt.converter.mvt.MvtUtils;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.metadata.stream.PhysicalLevelTechnique;\nimport org.maplibre.mlt.metadata.tileset.MltMetadata;\nimport org.maplibre.mlt.util.ByteArrayUtil;\nimport org.maplibre.mlt.util.StreamUtil;\n\npublic class StringDecoderTest {\n\n  public static Pair<Integer, ArrayList<byte[]>> encodeSharedDictionary(\n      List<List<String>> values,\n      PhysicalLevelTechnique physicalLevelTechnique,\n      boolean useFsstEncoding)\n      throws IOException {\n    return StringEncoder.encodeSharedDictionary(values, physicalLevelTechnique, useFsstEncoding);\n  }\n\n  @Test\n  @Disabled(\"Dictionary decoding to a scalar column is not implemented yet\")\n  public void decodeSharedDictionary_FsstDictionaryEncoded() throws IOException {\n    var values1 =\n        List.of(\n            \"TestTestTestTestTestTest\",\n            \"TestTestTestTestTestTest1\",\n            \"TestTestTestTestTestTest2\",\n            \"TestTestTestTestTestTest2\",\n            \"TestTestTestTestTestTest4\");\n    var values2 =\n        List.of(\n            \"TestTestTestTestTestTest6\",\n            \"TestTestTestTestTestTest5\",\n            \"TestTestTestTestTestTest8\",\n            \"TestTestTestTestTestTes9\",\n            \"TestTestTestTestTestTest10\");\n    var values = List.of(values1, values2);\n    var encodedValues = encodeSharedDictionary(values, PhysicalLevelTechnique.FAST_PFOR, true);\n\n    final var isNullable = true;\n    final var tileMetadata =\n        new MltMetadata.Column(\n            new MltMetadata.Field(\n                MltMetadata.scalarFieldType(MltMetadata.ScalarType.STRING, isNullable), \"Test\"));\n\n    final var decodedValues =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), tileMetadata);\n\n    final var v = decodedValues.getRight();\n    Assert.equals(values1, v.get(\":Test\"));\n    Assert.equals(values2, v.get(\":Test2\"));\n  }\n\n  @Test\n  public void decodeSharedDictionary_DictionaryEncoded() throws IOException {\n    final var values1 = List.of(\"Test\", \"Test2\", \"Test4\", \"Test2\", \"Test\");\n    final var values2 = List.of(\"Test1\", \"Test2\", \"Test1\", \"Test5\", \"Test\");\n    final var values = List.of(values1, values2);\n    final var encodedValues =\n        encodeSharedDictionary(values, PhysicalLevelTechnique.FAST_PFOR, false);\n\n    final var test = createField(\"Test\", MltMetadata.ScalarType.STRING, true);\n    final var test2 = createField(\"Test2\", MltMetadata.ScalarType.STRING, true);\n    final var tileMetadata =\n        new MltMetadata.Column(\n            new MltMetadata.Field(MltMetadata.structFieldType(List.of(test, test2)), \"Parent\"));\n\n    final var decodedValues =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), tileMetadata);\n\n    final var v = decodedValues.getRight();\n    Assert.equals(values1, v.get(\"ParentTest\"));\n    Assert.equals(values2, v.get(\"ParentTest2\"));\n  }\n\n  private MltMetadata.Field createField(\n      String name,\n      @SuppressWarnings(\"SameParameterValue\") MltMetadata.ScalarType type,\n      boolean isNullable) {\n    return new MltMetadata.Field(MltMetadata.scalarFieldType(type, isNullable), name);\n  }\n\n  private MltMetadata.ComplexField createComplexColumn(MltMetadata.Field... fields) {\n    return new MltMetadata.ComplexField(MltMetadata.ComplexType.STRUCT, Arrays.asList(fields));\n  }\n\n  @Test\n  public void decodeSharedDictionary_NullValues_DictionaryEncoded() throws IOException {\n    final var values1 = Arrays.asList(\"Test\", null, \"Test2\", null, \"Test4\", \"Test2\", \"Test\");\n    final var values2 =\n        Arrays.asList(\n            null, \"Test1\", \"Test2\", \"Test1\", null, null, \"Test5\", null, \"Test\", null, null);\n    final var values = List.of(values1, values2);\n    final var encodedValues =\n        encodeSharedDictionary(values, PhysicalLevelTechnique.FAST_PFOR, false);\n\n    final var test = createField(\"Test\", MltMetadata.ScalarType.STRING, true);\n    final var test2 = createField(\"Test2\", MltMetadata.ScalarType.STRING, true);\n    final var tileMetadata =\n        new MltMetadata.Column(\n            new MltMetadata.Field(MltMetadata.structFieldType(List.of(test, test2)), \"Parent\"));\n\n    final var decodeResults =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), tileMetadata);\n    final var decodedPresentValues = decodeResults.getMiddle();\n    final var decodedValues = decodeResults.getRight();\n\n    final var actualValues1 = decodedValues.get(\"ParentTest\");\n    final var p1 = decodedPresentValues.get(\"ParentTest\");\n    final var decodedV1 = new ArrayList<String>();\n    var counter = 0;\n    for (var i = 0; i < decodedPresentValues.size(); i++) {\n      decodedV1.add(p1.get(i) ? actualValues1.get(counter++) : null);\n    }\n    Assert.equals(decodedV1, new ArrayList<>(Arrays.asList(\"Test\", null)));\n\n    final var actualValues2 = decodedValues.get(\"ParentTest2\");\n    final var p2 = decodedPresentValues.get(\"ParentTest2\");\n    final var decodedV2 = new ArrayList<String>();\n    var counter2 = 0;\n    for (var i = 0; i < decodedPresentValues.size(); i++) {\n      decodedV2.add(p2.get(i) ? actualValues2.get(counter2++) : null);\n    }\n    Assert.equals(decodedV2, new ArrayList<>(Arrays.asList(null, null)));\n\n    Assert.equals(values1, decodedValues.get(\"ParentTest\"));\n    Assert.equals(values2, decodedValues.get(\"ParentTest2\"));\n  }\n\n  @Test\n  public void decodeSharedDictionary_NullValues_FsstDictionaryEncoded() throws IOException {\n    final var values1 =\n        Arrays.asList(\n            null,\n            null,\n            null,\n            null,\n            \"TestTestTestTestTestTest\",\n            \"TestTestTestTestTestTest1\",\n            null,\n            \"TestTestTestTestTestTest2\",\n            \"TestTestTestTestTestTest2\",\n            \"TestTestTestTestTestTest4\");\n    final var values2 =\n        Arrays.asList(\n            \"TestTestTestTestTestTest6\",\n            null,\n            \"TestTestTestTestTestTest5\",\n            \"TestTestTestTestTestTest8\",\n            \"TestTestTestTestTestTes9\",\n            null,\n            \"TestTestTestTestTestTest10\");\n    final var values = List.of(values1, values2);\n    final var encodedValues =\n        encodeSharedDictionary(values, PhysicalLevelTechnique.FAST_PFOR, true);\n\n    final var test = createField(\"Test\", MltMetadata.ScalarType.STRING, true);\n    final var test2 = createField(\"Test2\", MltMetadata.ScalarType.STRING, true);\n    final var tileMetadata =\n        new MltMetadata.Column(\n            new MltMetadata.Field(MltMetadata.structFieldType(List.of(test, test2)), \"Parent\"));\n\n    final var decodeResult =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), tileMetadata);\n\n    final var decodedValues = decodeResult.getRight();\n    final var decodedPresentValues = decodeResult.getMiddle();\n\n    var actualValues1 = decodedValues.get(\"ParentTest\");\n    var p1 = decodedPresentValues.get(\"ParentTest\");\n    var decodedV1 = new ArrayList<String>();\n    var counter = 0;\n    for (var i = 0; i < decodedPresentValues.size(); i++) {\n      decodedV1.add(p1.get(i) ? actualValues1.get(counter++) : null);\n    }\n    Assert.equals(decodedV1, new ArrayList<>(Arrays.asList(null, null)));\n\n    var actualValues2 = decodedValues.get(\"ParentTest2\");\n    var p2 = decodedPresentValues.get(\"ParentTest2\");\n    var decodedV2 = new ArrayList<String>();\n    var counter2 = 0;\n    for (var i = 0; i < decodedPresentValues.size(); i++) {\n      decodedV2.add(p2.get(i) ? actualValues2.get(counter2++) : null);\n    }\n    Assert.equals(decodedV2, new ArrayList<>(Arrays.asList(\"TestTestTestTestTestTest6\", null)));\n\n    Assert.equals(values1, decodedValues.get(\"ParentTest\"));\n    Assert.equals(values2, decodedValues.get(\"ParentTest2\"));\n  }\n\n  @ParameterizedTest\n  @EnumSource(\n      value = PhysicalLevelTechnique.class,\n      names = {\"VARINT\", \"FAST_PFOR\"})\n  public void decodeSharedDictionary_Mvt(PhysicalLevelTechnique technique) throws IOException {\n    final var tileId = String.format(\"%s_%s_%s\", 5, 16, 21);\n    final var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    final var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n    final var values =\n        mvTile\n            .getLayerStream()\n            .findFirst()\n            .orElseThrow(() -> new IllegalArgumentException(\"Expected at least one layer\"))\n            .features()\n            .stream()\n            .flatMap(\n                feature ->\n                    feature\n                        .getPropertyStream()\n                        .map(p -> p.getValue(feature.getIndex()))\n                        .flatMap(StreamUtil.ofType(String.class)))\n            .toList();\n    final var encodedValues = encodeSharedDictionary(List.of(values), technique, false);\n    final var isNullable = true;\n    final var tileMetadata =\n        new MltMetadata.Column(\n            new MltMetadata.Field(\n                MltMetadata.structFieldType(\n                    List.of(createField(\"TestChild\", MltMetadata.ScalarType.STRING, isNullable))),\n                \"TestParent:\"));\n    var decodeResult =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), tileMetadata);\n    var decodedValues = decodeResult.getRight().get(\"TestParent:TestChild\");\n    Assert.equals(values, decodedValues);\n  }\n\n  @ParameterizedTest\n  @ValueSource(strings = {\"water_name\", \"place\"})\n  public void decodeSharedDictionary_MvtWithNestedColumns(String tableName) throws IOException {\n    final var tileId = String.format(\"%s_%s_%s\", 5, 16, 21);\n    final var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n\n    // Force coverage of the case where the base mapped column does not appear in the first\n    // feature, causing the metadata field to need to be modified when it is found on a later one.\n    final var filter =\n        new TestUtils.TileFilter() {\n          private boolean matched = false;\n\n          @Override\n          public boolean test(\n              Layer layer, Feature feature, String propertyKey, Object propertyValue) {\n            if (!matched && layer.name().equals(tableName) && propertyKey.equals(\"name\")) {\n              matched = true;\n              return false;\n            }\n            return true;\n          }\n        };\n    final var mvTile = TestUtils.filterTile(MvtUtils.decodeMvt(mvtFilePath), filter);\n\n    final var columnMapping = new ColumnMapping(\"name\", \":\", true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(columnMapping));\n    final var tileMetadata = MltConverter.createTilesetMetadata(mvTile, columnMappings, true);\n    final var featureTable =\n        tileMetadata.featureTables.stream()\n            .filter(t -> t.name().equals(tableName))\n            .findFirst()\n            .orElseThrow(\n                () -> new IllegalArgumentException(\"Expected feature table  \" + tableName));\n    final var fieldMetadata =\n        featureTable.columns().stream()\n            .filter(f -> Objects.equals(f.getName(), \"name\"))\n            .findFirst()\n            .orElseThrow();\n\n    final var layer =\n        mvTile\n            .getLayerStream()\n            .filter(t -> t.name().equals(tableName))\n            .findFirst()\n            .orElseThrow(() -> new IllegalArgumentException(\"Expected layer  \" + tableName));\n\n    final var sharedValues =\n        new ArrayList<List<String>>(fieldMetadata.field().type().complexType().children().size());\n    for (var column : fieldMetadata.field().type().complexType().children()) {\n      var values = new ArrayList<String>();\n      for (var feature : layer.features()) {\n        values.add(\n            feature\n                .findProperty(\n                    fieldMetadata.getName() + column.name(), MltMetadata.ScalarType.STRING)\n                .map(p -> p.getValue(feature.getIndex()))\n                .flatMap(StreamUtil.optionalOfType(String.class))\n                .orElse(null));\n      }\n      sharedValues.add(values);\n    }\n\n    final var encodedValues =\n        encodeSharedDictionary(sharedValues, PhysicalLevelTechnique.FAST_PFOR, false);\n    Assert.isTrue(encodedValues.getLeft() > 2);\n\n    final var decodeResult =\n        StringDecoder.decodeSharedDictionary(\n            ByteArrayUtil.concat(encodedValues.getRight()), new IntWrapper(0), fieldMetadata);\n    final var decodedValues = decodeResult.getRight();\n\n    for (var column : fieldMetadata.field().type().complexType().children()) {\n      var i = 0;\n      for (var feature : layer.features()) {\n        final var propertyName = fieldMetadata.getName() + column.name();\n        final var expectedValue =\n            feature\n                .findProperty(propertyName, MltMetadata.ScalarType.STRING)\n                .map(p -> p.getValue(feature.getIndex()))\n                .flatMap(StreamUtil.optionalOfType(String.class))\n                .orElse(null);\n        final var field = decodedValues.get(propertyName);\n        Assert.isTrue(expectedValue == null || field != null);\n        final var actualValue = field.get(i++);\n        if (expectedValue != null || actualValue != null) {\n          Assert.equals(expectedValue, actualValue);\n        }\n      }\n    }\n  }\n\n  /// Apply multiple column mappings with the same prefix\n  @Test\n  public void decodeColumnMap_Mvt_prefix_multi() throws IOException {\n    final var tileId = String.format(\"%s_%s_%s\", 5, 16, 21);\n    final var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    final var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var mapping1 = new ColumnMapping(\"name\", \":\", true);\n    final var mapping2 = new ColumnMapping(\"name\", \"_\", true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(mapping1, mapping2));\n\n    final var metadata =\n        MltConverter.createTilesetMetadata(mvTile, columnMappings, /*isIdPresent*/ true);\n\n    final var expected =\n        Map.of(\n            \"water_name:name\", 59,\n            \"water_name:name_\", 3,\n            \"place:name\", 68,\n            \"place:name_\", 3);\n    int found = 0;\n    for (var table : metadata.featureTables) {\n      for (var column : table.columns()) {\n        if (column.is(MltMetadata.ComplexType.STRUCT)) {\n          final var complex = column.field().type().complexType();\n          final var fieldKey = table.name() + \":\" + column.getName();\n          Assert.equals(\n              expected.get(fieldKey),\n              complex.children().size(),\n              \"Unexpected number of children in \" + fieldKey);\n          found++;\n        }\n      }\n    }\n    Assert.equals(expected.size(), found);\n  }\n\n  /// Apply explicit column mappings\n  @Test\n  public void decodeColumnMap_Mvt_explicit() throws IOException {\n    final var tileId = String.format(\"%s_%s_%s\", 5, 16, 21);\n    final var mvtFilePath = Paths.get(TestSettings.OMT_MVT_PATH, tileId + \".mvt\");\n    final var mvTile = MvtUtils.decodeMvt(mvtFilePath);\n\n    final var columnMapping =\n        new ColumnMapping(List.of(\"name\", \"name:en\", \"name:latin\", \"name_en\", \"name_int\"), true);\n    final var columnMappings =\n        ColumnMappingConfig.of(Pattern.compile(\".*\"), List.of(columnMapping));\n\n    final var metadata =\n        MltConverter.createTilesetMetadata(mvTile, columnMappings, /*isIdPresent*/ true);\n\n    final var expected =\n        Map.of(\n            \"water_name:name\", 5,\n            \"place:name\", 5);\n    int found = 0;\n    for (var table : metadata.featureTables) {\n      for (var column : table.columns()) {\n        if (column.is(MltMetadata.ComplexType.STRUCT)) {\n          final var complex = column.field().type().complexType();\n          final var fieldKey = table.name() + \":\" + column.getName();\n          Assert.isTrue(expected.containsKey(fieldKey));\n          Assert.equals(\n              expected.get(fieldKey),\n              complex.children().size(),\n              \"Unexpected number of children in \" + fieldKey);\n          found++;\n        }\n      }\n    }\n    Assert.equals(expected.size(), found);\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/json/JsonTest.java",
    "content": "package org.maplibre.mlt.json;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.jupiter.api.Test;\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MLTFeature;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.data.MapboxVectorTile;\n\nclass JsonTest {\n  private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();\n\n  @Test\n  void propertyValues() {\n    final Map<String, Object> properties = new HashMap<>();\n    properties.put(\"name\", \"Main St\");\n    properties.put(\"nullValue\", null);\n\n    final var tile =\n        mltOf(\n            layer(\n                \"roads\",\n                4096,\n                feature(7, GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2)), properties)));\n    final JsonObject root = JsonParser.parseString(Json.toJson(tile, false)).getAsJsonObject();\n    final var layer = root.getAsJsonArray(\"layers\").get(0).getAsJsonObject();\n    final var feature = layer.getAsJsonArray(\"features\").get(0).getAsJsonObject();\n\n    assertEquals(\"roads\", layer.get(\"name\").getAsString());\n    assertEquals(4096, layer.get(\"extent\").getAsInt());\n    assertEquals(7L, feature.get(\"id\").getAsLong());\n    assertEquals(\"POINT (1 2)\", feature.get(\"geometry\").getAsString());\n    assertEquals(\"Main St\", feature.getAsJsonObject(\"properties\").get(\"name\").getAsString());\n    assertFalse(feature.getAsJsonObject(\"properties\").has(\"nullValue\"));\n  }\n\n  @Test\n  void mvtSerializesLayers() {\n    final var tile =\n        mvtOf(\n            layer(\n                \"water\",\n                4096,\n                feature(\n                    GEOMETRY_FACTORY.createPoint(new Coordinate(5, 6)), Map.of(\"class\", \"river\"))));\n\n    final JsonObject root = JsonParser.parseString(Json.toJson(tile, false)).getAsJsonObject();\n    final var layer = root.getAsJsonArray(\"layers\").get(0).getAsJsonObject();\n\n    assertEquals(\"water\", layer.get(\"name\").getAsString());\n    assertEquals(1, layer.getAsJsonArray(\"features\").size());\n    assertEquals(\n        \"river\",\n        layer\n            .getAsJsonArray(\"features\")\n            .get(0)\n            .getAsJsonObject()\n            .getAsJsonObject(\"properties\")\n            .get(\"class\")\n            .getAsString());\n  }\n\n  @Test\n  void featureCollectionWithLayerMetadata() {\n    final var tile =\n        mltOf(\n            layer(\n                \"buildings\",\n                8192,\n                feature(\n                    10,\n                    GEOMETRY_FACTORY.createPoint(new Coordinate(12, 34)),\n                    Map.of(\"height\", 15))));\n\n    final JsonObject root = JsonParser.parseString(Json.toGeoJson(tile, false)).getAsJsonObject();\n    final var feature = root.getAsJsonArray(\"features\").get(0).getAsJsonObject();\n    final var properties = feature.getAsJsonObject(\"properties\");\n\n    assertEquals(\"FeatureCollection\", root.get(\"type\").getAsString());\n    assertEquals(\"Feature\", feature.get(\"type\").getAsString());\n    assertEquals(10L, feature.get(\"id\").getAsLong());\n    assertEquals(\"buildings\", properties.get(\"_layer\").getAsString());\n    assertEquals(8192, properties.get(\"_extent\").getAsInt());\n    assertEquals(15, properties.get(\"height\").getAsInt());\n    assertEquals(\"Point\", feature.getAsJsonObject(\"geometry\").get(\"type\").getAsString());\n  }\n\n  @Test\n  void floatAndDoublePropertiesToTokens() {\n    final var nested = new LinkedHashMap<String, Object>();\n    nested.put(\"inner\", Double.NEGATIVE_INFINITY);\n\n    final var properties = new LinkedHashMap<String, Object>();\n    properties.put(\"f32nan\", Float.NaN);\n    properties.put(\"f32inf\", Float.POSITIVE_INFINITY);\n    properties.put(\"f32neg\", Float.NEGATIVE_INFINITY);\n    properties.put(\"f64nan\", Double.NaN);\n    properties.put(\"f64inf\", Double.POSITIVE_INFINITY);\n    properties.put(\"f64neg\", Double.NEGATIVE_INFINITY);\n    properties.put(\"f32\", 1.5f);\n    properties.put(\"f64\", -1.5);\n\n    final var tile =\n        mltOf(\n            layer(\n                \"roads\",\n                4096,\n                feature(1, GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0)), properties)));\n\n    final var props =\n        JsonParser.parseString(Json.toGeoJson(tile, false))\n            .getAsJsonObject()\n            .getAsJsonArray(\"features\")\n            .get(0)\n            .getAsJsonObject()\n            .getAsJsonObject(\"properties\");\n\n    assertEquals(\"1.5\", props.get(\"f32\").getAsJsonPrimitive().toString());\n    assertEquals(\"f32::NAN\", props.get(\"f32nan\").getAsString());\n    assertEquals(\"f32::INFINITY\", props.get(\"f32inf\").getAsString());\n    assertEquals(\"f32::NEG_INFINITY\", props.get(\"f32neg\").getAsString());\n    assertEquals(\"-1.5\", props.get(\"f64\").getAsJsonPrimitive().toString());\n    assertEquals(\"f64::NAN\", props.get(\"f64nan\").getAsString());\n    assertEquals(\"f64::INFINITY\", props.get(\"f64inf\").getAsString());\n    assertEquals(\"f64::NEG_INFINITY\", props.get(\"f64neg\").getAsString());\n  }\n\n  @Test\n  void nullGeometry() {\n    final var tile = mltOf(layer(\"places\", 4096, feature(5, null, Map.of(\"name\", \"unknown\"))));\n\n    final var feature =\n        JsonParser.parseString(Json.toGeoJson(tile, false))\n            .getAsJsonObject()\n            .getAsJsonArray(\"features\")\n            .get(0)\n            .getAsJsonObject();\n\n    assertFalse(feature.has(\"geometry\"));\n  }\n\n  @Test\n  void integerCoordinates() {\n    final var line =\n        GEOMETRY_FACTORY.createLineString(\n            new Coordinate[] {new Coordinate(1.0, 2.0), new Coordinate(3.5, 4.0)});\n    final var tile = mltOf(layer(\"roads\", 4096, feature(1, line, Map.of())));\n\n    final var coordinates =\n        JsonParser.parseString(Json.toGeoJson(tile, false))\n            .getAsJsonObject()\n            .getAsJsonArray(\"features\")\n            .get(0)\n            .getAsJsonObject()\n            .getAsJsonObject(\"geometry\")\n            .getAsJsonArray(\"coordinates\");\n\n    assertEquals(\"1\", coordinates.get(0).getAsJsonArray().get(0).getAsJsonPrimitive().toString());\n    assertEquals(\"2\", coordinates.get(0).getAsJsonArray().get(1).getAsJsonPrimitive().toString());\n    assertEquals(\"3.5\", coordinates.get(1).getAsJsonArray().get(0).getAsJsonPrimitive().toString());\n    assertEquals(\"4\", coordinates.get(1).getAsJsonArray().get(1).getAsJsonPrimitive().toString());\n  }\n\n  @Test\n  void propertiesAlphabetical() {\n    final var properties = new LinkedHashMap<String, Object>();\n    properties.put(\"zeta\", 3);\n    properties.put(\"alpha\", 1);\n    properties.put(\"middle\", 2);\n\n    final var tile =\n        mltOf(\n            layer(\n                \"roads\",\n                4096,\n                feature(1, GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0)), properties)));\n\n    final var json = Json.toGeoJson(tile, false);\n    final var propertiesStart = json.indexOf(\"\\\"properties\\\":{\");\n    final var geometryStart = json.indexOf(\",\\\"geometry\\\"\", propertiesStart);\n    final var propertiesJson = json.substring(propertiesStart, geometryStart);\n\n    final var alphaIndex = propertiesJson.indexOf(\"\\\"alpha\\\":\");\n    final var middleIndex = propertiesJson.indexOf(\"\\\"middle\\\":\");\n    final var zetaIndex = propertiesJson.indexOf(\"\\\"zeta\\\":\");\n\n    assertTrue(alphaIndex >= 0);\n    assertTrue(middleIndex >= 0);\n    assertTrue(zetaIndex >= 0);\n    assertTrue(alphaIndex < middleIndex);\n    assertTrue(middleIndex < zetaIndex);\n  }\n\n  @Test\n  void jsonElementStableOrder() {\n    final var tile =\n        mltOf(\n            layer(\n                \"roads\",\n                4096,\n                feature(\n                    7, GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2)), Map.of(\"name\", \"A\"))));\n\n    final var json = Json.toJson(tile, false);\n\n    final var layersIndex = json.indexOf(\"\\\"layers\\\":\");\n    final var nameIndex = json.indexOf(\"\\\"name\\\":\", layersIndex);\n    final var extentIndex = json.indexOf(\"\\\"extent\\\":\", nameIndex);\n    final var featuresIndex = json.indexOf(\"\\\"features\\\":\", extentIndex);\n    final var idIndex = json.indexOf(\"\\\"id\\\":\", featuresIndex);\n    final var geometryIndex = json.indexOf(\"\\\"geometry\\\":\", idIndex);\n    final var propertiesIndex = json.indexOf(\"\\\"properties\\\":\", geometryIndex);\n\n    assertTrue(layersIndex >= 0);\n    assertTrue(nameIndex > layersIndex);\n    assertTrue(extentIndex > nameIndex);\n    assertTrue(featuresIndex > extentIndex);\n    assertTrue(idIndex > featuresIndex);\n    assertTrue(geometryIndex > idIndex);\n    assertTrue(propertiesIndex > geometryIndex);\n  }\n\n  private static MapLibreTile mltOf(Layer... layers) {\n    return new MapLibreTile(List.of(layers));\n  }\n\n  private static MapboxVectorTile mvtOf(Layer... layers) {\n    return new MapboxVectorTile(List.of(layers));\n  }\n\n  private static Layer layer(String name, int extent, Feature... features) {\n    return new Layer(name, List.of(features), extent);\n  }\n\n  private static Feature feature(Geometry geometry, Map<String, Object> properties) {\n    return MLTFeature.builder().geometry(geometry).rawProperties(properties).index(0).build();\n  }\n\n  private static Feature feature(long id, Geometry geometry, Map<String, Object> properties) {\n    return MLTFeature.builder()\n        .id(id)\n        .geometry(geometry)\n        .rawProperties(properties)\n        .index(0)\n        .build();\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/metadata/tileset/MltMetadataColumnTest.java",
    "content": "package org.maplibre.mlt.metadata.tileset;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.List;\nimport org.junit.jupiter.api.Test;\n\nclass MltMetadataColumnTest {\n  @Test\n  void createScalarColumn() {\n    final var col =\n        new MltMetadata.Column(\n            new MltMetadata.Field(\n                MltMetadata.scalarFieldType(MltMetadata.ScalarType.STRING, true), \"name\"));\n    assertEquals(\"name\", col.getName());\n    assertTrue(col.isNullable());\n    assertEquals(MltMetadata.ColumnScope.FEATURE, col.columnScope());\n    assertNotNull(col.field().type().scalarType());\n    assertNull(col.field().type().complexType());\n  }\n\n  @Test\n  void createComplexColumn() {\n    final var child =\n        new MltMetadata.Field(MltMetadata.scalarFieldType(MltMetadata.ScalarType.INT_32, false));\n    final var col =\n        new MltMetadata.Column(\n            new MltMetadata.Field(MltMetadata.structFieldType(List.of(child)), \"geom\"),\n            MltMetadata.ColumnScope.VERTEX);\n\n    assertEquals(\"geom\", col.getName());\n    assertFalse(col.isNullable());\n    assertEquals(MltMetadata.ColumnScope.VERTEX, col.columnScope());\n    assertNotNull(col.field().type().complexType());\n    assertNull(col.field().type().scalarType());\n    assertEquals(MltMetadata.ComplexType.STRUCT, col.field().type().complexType().physicalType());\n    assertEquals(1, col.field().type().complexType().children().size());\n  }\n\n  @Test\n  void acceptsNullChildren() {\n    final var col = new MltMetadata.Column(MltMetadata.structFieldType(null));\n    assertNotNull(col.field().type().complexType());\n    assertEquals(MltMetadata.ComplexType.STRUCT, col.field().type().complexType().physicalType());\n    assertTrue(col.field().type().complexType().children().isEmpty());\n  }\n\n  @Test\n  void defaultsToFeatureScope() {\n    final var col =\n        new MltMetadata.Column(MltMetadata.scalarFieldType(MltMetadata.ScalarType.STRING, true));\n    assertEquals(MltMetadata.ColumnScope.FEATURE, col.columnScope());\n  }\n\n  @Test\n  void rejectsInvalidScope() {\n    assertThrows(\n        IllegalStateException.class,\n        () ->\n            new MltMetadata.Column(\n                new MltMetadata.Field(\n                    MltMetadata.scalarFieldType(MltMetadata.ScalarType.STRING, true)),\n                MltMetadata.ColumnScope.UNRECOGNIZED));\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/synthetics/SyntheticsTest.java",
    "content": "package org.maplibre.mlt.synthetics;\n\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.ToNumberPolicy;\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.PathMatcher;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.SequencedCollection;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.maplibre.mlt.data.MapLibreTile;\nimport org.maplibre.mlt.decoder.MltDecoder;\nimport org.maplibre.mlt.json.Json;\n\npublic class SyntheticsTest {\n  @Test\n  public void checkSynthetics() throws IOException {\n    final var matcher = FileSystems.getDefault().getPathMatcher(\"glob:**/*.mlt\");\n    final var rootPaths =\n        new String[] {\"../../test/synthetic/0x01\", \"../../test/synthetic/0x01-rust\"};\n    final var mltPaths =\n        Arrays.stream(rootPaths).flatMap(root -> findFiles(root, matcher).stream()).toList();\n    for (Path path : mltPaths) {\n      // Load the file\n      final MapLibreTile tile;\n      try {\n        tile = MltDecoder.decodeMlTile(Files.readAllBytes(path));\n      } catch (Exception e) {\n        Assertions.fail(\"Failed to decode \" + path, e);\n        continue;\n      }\n\n      final var baseName = path.getFileName().toString();\n      final var jsonPath =\n          path.resolveSibling(\n              baseName.substring(0, Math.max(0, baseName.lastIndexOf('.'))) + \".json\");\n      if (!Files.exists(jsonPath)) {\n        System.err.println(\"WARNING: No expected JSON for \" + path);\n        continue;\n      }\n\n      // Don't let Gson turn Uin64 values into doubles, which loses precision.\n      final var gson =\n          new GsonBuilder()\n              .serializeSpecialFloatingPointValues()\n              .setObjectToNumberStrategy(ToNumberPolicy.BIG_DECIMAL)\n              .create();\n\n      Object expectedJsonObjects;\n      try (var jsonReader = Files.newBufferedReader(jsonPath)) {\n        expectedJsonObjects = gson.fromJson(jsonReader, Object.class);\n      } catch (Exception e) {\n        Assertions.fail(\"Failed to read \" + jsonPath, e);\n        continue;\n      }\n\n      final var actualJsonObjects = Json.toGeoJsonObjects(tile, gson);\n\n      Assertions.assertTrue(\n          compareJsonObjects(expectedJsonObjects, actualJsonObjects),\n          \"JSON objects do not match for \" + path);\n    }\n  }\n\n  private static SequencedCollection<Path> findFiles(String root, PathMatcher matcher) {\n    try (var paths = Files.walk(Path.of(root))) {\n      return paths.filter(matcher::matches).filter(Files::isRegularFile).toList();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to walk \" + root, e);\n    }\n  }\n\n  private boolean compareJsonObjects(Object expected, Object actual) {\n    if (expected instanceof Map<?, ?> expectedMap && actual instanceof Map<?, ?> actualMap) {\n      if (expectedMap.size() != actualMap.size()) {\n        return false;\n      }\n      for (var entry : expectedMap.entrySet()) {\n        final var key = entry.getKey();\n        if (!actualMap.containsKey(key)) {\n          return false;\n        }\n        if (!compareJsonObjects(entry.getValue(), actualMap.get(key))) {\n          return false;\n        }\n      }\n      return true;\n    } else if (expected instanceof Iterable<?> expectedIterable\n        && actual instanceof Iterable<?> actualIterable) {\n      final var expectedIterator = expectedIterable.iterator();\n      final var actualIterator = actualIterable.iterator();\n      while (expectedIterator.hasNext() && actualIterator.hasNext()) {\n        if (!compareJsonObjects(expectedIterator.next(), actualIterator.next())) {\n          return false;\n        }\n      }\n      return !expectedIterator.hasNext() && !actualIterator.hasNext();\n    }\n    return Objects.equals(expected, actual) || numericsEqual(expected, actual);\n  }\n\n  private boolean numericsEqual(Object a, Object b) {\n    if (a instanceof Number numA && b instanceof Number numB) {\n      if (numA.doubleValue() == numB.doubleValue()) {\n        return true;\n      } else if (compareFloats(numA, numB) || compareFloats(numB, numA)) {\n        return true;\n      } else if (a instanceof BigDecimal || b instanceof BigDecimal) {\n        return compareDecimals(a, b) || compareDecimals(b, a);\n      }\n    }\n    return false;\n  }\n\n  private boolean compareFloats(Number a, Number b) {\n    if (a instanceof Double dbl && b instanceof Float flt) {\n      return dbl.floatValue() == flt;\n    }\n    return false;\n  }\n\n  /// Compare values loaded as BigDecimal, with\n  /// particular care for unsigned values loaded as, e.g., -1\n  private boolean compareDecimals(Object a, Object b) {\n    if (a instanceof BigDecimal aDec) {\n      if (b instanceof BigDecimal bDec) {\n        return aDec.compareTo(bDec) == 0;\n      } else if (b instanceof Float fltB) {\n        return aDec.floatValue() == fltB || aDec.compareTo(BigDecimal.valueOf(fltB)) == 0;\n      } else if (b instanceof Number numB) {\n        return numB.intValue() == aDec.intValue()\n            || numB.longValue() == aDec.longValue()\n            || aDec.compareTo(BigDecimal.valueOf(numB.longValue())) == 0\n            || aDec.compareTo(BigDecimal.valueOf(numB.doubleValue())) == 0;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/util/OptionalUtilTest.java",
    "content": "package org.maplibre.mlt.util;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.Optional;\nimport org.junit.jupiter.api.Test;\n\nclass OptionalUtilTest {\n  @Test\n  void isLessThanBothEmpty() {\n    assertFalse(OptionalUtil.isLessThan(Optional.<Integer>empty(), Optional.<Integer>empty()));\n  }\n\n  @Test\n  void isLessThan_whenLeftEmptyAndRightPresent_returnsFalse() {\n    assertFalse(OptionalUtil.isLessThan(Optional.<Integer>empty(), Optional.of(10)));\n  }\n\n  @Test\n  void isLessThan_whenLeftPresentAndRightEmpty_returnsTrue() {\n    assertTrue(OptionalUtil.isLessThan(Optional.of(10), Optional.<Integer>empty()));\n  }\n\n  @Test\n  void isLessThanLess() {\n    assertTrue(OptionalUtil.isLessThan(Optional.of(10), Optional.of(20)));\n  }\n\n  @Test\n  void isLessThanEqual() {\n    assertFalse(OptionalUtil.isLessThan(Optional.of(20), Optional.of(20)));\n  }\n\n  @Test\n  void isLessThanGreater() {\n    assertFalse(OptionalUtil.isLessThan(Optional.of(30), Optional.of(20)));\n  }\n\n  @Test\n  void isLessThanSupportsComparableTypes() {\n    assertTrue(OptionalUtil.isLessThan(Optional.of(\"a\"), Optional.of(\"b\")));\n    assertFalse(OptionalUtil.isLessThan(Optional.of(\"b\"), Optional.of(\"a\")));\n  }\n\n  @Test\n  void isLessThanThrowsWhenOptional1Nulls() {\n    assertThrows(NullPointerException.class, () -> OptionalUtil.isLessThan(null, Optional.of(1)));\n  }\n\n  @Test\n  void isLessThanThrowsWhenOptional2Null() {\n    assertThrows(NullPointerException.class, () -> OptionalUtil.isLessThan(Optional.of(1), null));\n  }\n\n  @Test\n  void mapReturnsMappedValue() {\n    final var result = OptionalUtil.map(Optional.of(2), Optional.of(3), Integer::sum);\n    assertEquals(Optional.of(5), result);\n  }\n\n  @Test\n  void mapEmptyWhenOptional1Empty() {\n    final var result = OptionalUtil.map(Optional.<Integer>empty(), Optional.of(3), Integer::sum);\n    assertEquals(Optional.empty(), result);\n  }\n\n  @Test\n  void mapEmptyWhenOptional2Empty() {\n    final var result = OptionalUtil.map(Optional.of(2), Optional.<Integer>empty(), Integer::sum);\n    assertEquals(Optional.empty(), result);\n  }\n\n  @Test\n  void mapBothEmpty() {\n    final var result =\n        OptionalUtil.map(Optional.<Integer>empty(), Optional.<Integer>empty(), Integer::sum);\n    assertEquals(Optional.empty(), result);\n  }\n\n  @Test\n  void mapThrowsWhenOptional1Null() {\n    assertThrows(\n        NullPointerException.class, () -> OptionalUtil.map(null, Optional.of(1), Integer::sum));\n  }\n\n  @Test\n  void mapThrowsWhenOptional2Null() {\n    assertThrows(\n        NullPointerException.class, () -> OptionalUtil.map(Optional.of(1), null, Integer::sum));\n  }\n\n  @Test\n  void mapThrowsWhenFunctionNull() {\n    assertThrows(\n        NullPointerException.class, () -> OptionalUtil.map(Optional.of(1), Optional.of(2), null));\n  }\n}\n"
  },
  {
    "path": "java/mlt-core/src/test/java/org/maplibre/mlt/util/StreamUtilTest.java",
    "content": "package org.maplibre.mlt.util;\n\nimport static org.junit.jupiter.api.Assertions.*;\n\nimport java.util.ArrayList;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\nimport org.apache.commons.lang3.mutable.MutableBoolean;\nimport org.apache.commons.lang3.mutable.MutableInt;\nimport org.apache.commons.lang3.tuple.Pair;\nimport org.junit.jupiter.api.Test;\n\nclass StreamUtilTest {\n  @Test\n  void zipWithEqualSizedStreams() {\n    final var result =\n        StreamUtil.zip(Stream.of(1, 2, 3), Stream.of(\"a\", \"b\", \"c\"), (x, y) -> x + \":\" + y)\n            .toList();\n    assertEquals(3, result.size());\n    assertEquals(\"1:a\", result.get(0));\n    assertEquals(\"2:b\", result.get(1));\n    assertEquals(\"3:c\", result.get(2));\n  }\n\n  @Test\n  void zipPairs() {\n    final var result = StreamUtil.zip(Stream.of(1, 2, 3), Stream.of(\"a\", \"b\", \"c\")).toList();\n    assertEquals(3, result.size());\n    assertEquals(Pair.of(1, \"a\"), result.get(0));\n    assertEquals(Pair.of(2, \"b\"), result.get(1));\n    assertEquals(Pair.of(3, \"c\"), result.get(2));\n  }\n\n  @Test\n  void zipWithEmptyStreams() {\n    assertEquals(0, StreamUtil.zip(Stream.of(), Stream.of(), (x, y) -> 0).toList().size());\n  }\n\n  @Test\n  void zipWithFirstStreamShorter() {\n    final var result =\n        StreamUtil.zip(Stream.of(1, 2), Stream.of(\"a\", \"b\", \"c\"), (x, y) -> x + \":\" + y).toList();\n    assertEquals(2, result.size());\n    assertEquals(\"1:a\", result.get(0));\n    assertEquals(\"2:b\", result.get(1));\n  }\n\n  @Test\n  void zipWithSecondStreamShorter() {\n    final var result =\n        StreamUtil.zip(Stream.of(1, 2, 3), Stream.of(\"a\", \"b\"), (x, y) -> x + \":\" + y).toList();\n    assertEquals(2, result.size());\n    assertEquals(\"1:a\", result.get(0));\n    assertEquals(\"2:b\", result.get(1));\n  }\n\n  @Test\n  void zipWithTransformationFunction() {\n    final var result =\n        StreamUtil.zip(Stream.of(10, 20, 30), Stream.of(1, 2, 3), (x, y) -> x + y).toList();\n    assertEquals(3, result.size());\n    assertEquals(11, result.get(0));\n    assertEquals(22, result.get(1));\n    assertEquals(33, result.get(2));\n  }\n\n  @Test\n  void zipPreservesStreamCharacteristics() {\n    final var result =\n        StreamUtil.zip(Stream.of(1, 2, 3), Stream.of(\"a\", \"b\", \"c\"), (x, y) -> x + \":\" + y)\n            .toList();\n    assertNotNull(result);\n    assertEquals(3, result.size());\n  }\n\n  void zipWithParallelStreams(boolean first, boolean second) {\n    final var a = Stream.of(1, 2, 3, 4, 5).parallel();\n    final var b = Stream.of(\"a\", \"b\", \"c\", \"d\", \"e\").parallel();\n    final var result =\n        StreamUtil.zip(first ? a.parallel() : a, second ? b.parallel() : b, (x, y) -> x + \":\" + y)\n            .toList();\n    // Results should contain all expected pairs (order may vary due to parallel processing)\n    assertEquals(5, result.size());\n    assertTrue(result.contains(\"1:a\"));\n    assertTrue(result.contains(\"2:b\"));\n    assertTrue(result.contains(\"3:c\"));\n    assertTrue(result.contains(\"4:d\"));\n    assertTrue(result.contains(\"5:e\"));\n  }\n\n  @Test\n  void zipWithFirstParallelStream() {\n    zipWithParallelStreams(true, false);\n  }\n\n  @Test\n  void zipWithSecondParallelStream() {\n    zipWithParallelStreams(false, true);\n  }\n\n  @Test\n  void zipWithBothParallelStreams() {\n    zipWithParallelStreams(true, true);\n  }\n\n  @Test\n  void zipWithNullTransformationFunction() {\n    assertThrows(\n        NullPointerException.class,\n        () -> StreamUtil.zip(Stream.of(1, 2, 3), Stream.of(\"a\", \"b\", \"c\"), null));\n  }\n\n  @Test\n  void zipWithNullFirstStream() {\n    assertThrows(\n        NullPointerException.class, () -> StreamUtil.zip(null, Stream.of(), (x, y) -> x + \":\" + y));\n  }\n\n  @Test\n  void zipWithNullSecondStream() {\n    assertThrows(\n        NullPointerException.class, () -> StreamUtil.zip(Stream.of(), null, (x, y) -> x + \":\" + y));\n  }\n\n  @Test\n  void zipEachProcessesAllPairs() {\n    final var processed = new ArrayList<>();\n    final long count =\n        StreamUtil.zipEach(\n            Stream.of(1, 2, 3), Stream.of(\"a\", \"b\", \"c\"), (x, y) -> processed.add(x + \":\" + y));\n    assertEquals(3, count);\n  }\n\n  @Test\n  void zipEachWithEmptyStreams() {\n    assertEquals(0, StreamUtil.zipEach(Stream.of(), Stream.of(), (x, y) -> {}));\n  }\n\n  @Test\n  void zipEachWithMismatchedKnownStreamSizes() {\n    final var sideEffectCount = new MutableInt(0);\n    final long count =\n        StreamUtil.zipEach(\n            Stream.of(1, 2, 3), Stream.of(\"a\", \"b\"), (x, y) -> sideEffectCount.increment());\n    // With the new behavior, we process all pairs from the shorter stream\n    assertEquals(2, count);\n    assertEquals(2, sideEffectCount.get());\n  }\n\n  @Test\n  void zipEachWithMismatchedUnknownStreamSizes() {\n    final var sideEffectCount = new MutableInt(0);\n    final var a = Stream.iterate(0, i -> i < 3, i -> i + 1);\n    final var b = Stream.iterate(0, i -> i < 2, i -> i + 1);\n    // Streams with different sizes now silently truncate to the shorter stream\n    final long count = StreamUtil.zipEach(a, b, (x, y) -> sideEffectCount.increment());\n    // With the new behavior, we process all pairs from the shorter stream\n    assertEquals(2, count);\n    assertEquals(2, sideEffectCount.get());\n  }\n\n  @Test\n  void zipEachWithNullFirstStream() {\n    assertThrows(\n        NullPointerException.class, () -> StreamUtil.zipEach(null, Stream.of(), (x, y) -> {}));\n  }\n\n  @Test\n  void zipEachWithNullSecondStream() {\n    assertThrows(\n        NullPointerException.class, () -> StreamUtil.zipEach(Stream.of(), null, (x, y) -> {}));\n  }\n\n  @Test\n  void zipWithLargeStreams() {\n    final var a = Stream.iterate(0, i -> i + 1).limit(1000);\n    final var b = Stream.iterate(1000, i -> i + 1).limit(1000);\n    final var result = StreamUtil.zip(a, b, Integer::sum).toList();\n    assertEquals(1000, result.size());\n    assertEquals(1000, result.get(0)); // First pair: 0 + 1000 = 1000\n    assertEquals(1008, result.get(4)); // Fifth pair: 4 + 1004 = 1008\n    assertEquals(2998, result.get(999)); // Last pair: 999 + 1999 = 2998\n  }\n\n  @Test\n  void zipHandlesNullValuesInStreams() {\n    final var result =\n        StreamUtil.zip(Stream.of(\"a\", null, \"c\"), Stream.of(\"1\", \"2\", \"3\"), (x, y) -> x + \":\" + y)\n            .toList();\n    assertEquals(3, result.size());\n    assertEquals(\"a:1\", result.get(0));\n    assertEquals(\"null:2\", result.get(1));\n    assertEquals(\"c:3\", result.get(2));\n  }\n\n  @Test\n  void zipWithDifferentObjectTypes() {\n    final var result =\n        StreamUtil.zip(\n                Stream.of(\"one\", \"two\", \"three\"), Stream.of(1.5, 2.5, 3.5), (s, d) -> s + \"=\" + d)\n            .toList();\n    assertEquals(3, result.size());\n    assertEquals(\"one=1.5\", result.get(0));\n    assertEquals(\"two=2.5\", result.get(1));\n    assertEquals(\"three=3.5\", result.get(2));\n  }\n\n  @Test\n  void zipStreamIsLazy() {\n    final var functionCalled = new MutableBoolean(false);\n    final var zipped =\n        StreamUtil.zip(\n            Stream.of(1, 2, 3),\n            Stream.of(\"a\", \"b\", \"c\"),\n            (x, y) -> {\n              functionCalled.setTrue();\n              return x + \":\" + y;\n            });\n\n    final var split = zipped.spliterator();\n    // size is known before evaluation for fixed-size inputs\n    assertEquals(3, split.getExactSizeIfKnown());\n\n    // Function should not be called yet - stream is lazy\n    assertFalse(functionCalled.get());\n\n    // Function should be called when stream is consumed\n    final var result = StreamSupport.stream(split, false).toList();\n    assertTrue(functionCalled.get());\n    assertEquals(3, result.size());\n    assertEquals(\"3:c\", result.get(2));\n  }\n\n  @Test\n  void zipWithInfiniteFirstStream() {\n    final var infiniteStream = Stream.generate(() -> \"x\");\n    final var finiteStream = Stream.of(1, 2, 3, 4, 5);\n    final var result = StreamUtil.zip(infiniteStream, finiteStream, (x, y) -> y + \":\" + x).toList();\n    assertEquals(5, result.size());\n    assertEquals(\"1:x\", result.get(0));\n    assertEquals(\"5:x\", result.get(4));\n  }\n\n  @Test\n  void zipWithInfiniteSecondStream() {\n    // Test with Stream.generate - an infinite stream as the second argument\n    final var finiteStream = Stream.of(\"a\", \"b\", \"c\");\n    final var infiniteStream = Stream.generate(() -> 42);\n    final var result = StreamUtil.zip(finiteStream, infiniteStream, (x, y) -> x + \":\" + y).toList();\n    assertEquals(3, result.size());\n    assertEquals(\"a:42\", result.get(0));\n    assertEquals(\"c:42\", result.get(2));\n  }\n}\n"
  },
  {
    "path": "java/mlt-tools/build.gradle",
    "content": "plugins {\n    alias(libs.plugins.diffplug.spotless)\n    id 'java-library'\n}\n\nrepositories {\n    mavenCentral()\n    maven {\n        url = 'https://maven.ecc.no/releases'\n    }\n    maven {\n        url = 'https://repo.osgeo.org/repository/release/'\n        content {\n            includeGroup 'org.geotools'\n            includeGroup 'org.geotools.ogc'\n            includeGroup 'org.eclipse.imagen'\n            includeGroup 'it.geosolutions.jgridshift'\n        }\n    }\n}\n\ndependencies {\n    implementation project(':mlt-core')\n    implementation(libs.jts.core)\n}\n\njava {\n    toolchain {\n        languageVersion = JavaLanguageVersion.of(21)\n    }\n}\n\nspotless {\n    java {\n        importOrder()\n        target 'src/*/java/**/*.java'\n        googleJavaFormat()\n        removeUnusedImports()\n    }\n}\n\ngradle.projectsEvaluated {\n    tasks.withType(JavaCompile).tap {\n        configureEach {\n            options.compilerArgs << \"-Xlint:unchecked\"\n            options.compilerArgs << \"-Xlint:deprecation\"\n            options.compilerArgs << \"-Xlint:all\"\n        }\n    }\n}\n\ntasks.register('generateSyntheticMlt', JavaExec) {\n    description = 'Generates synthetic MLT test fixtures'\n    classpath = sourceSets.main.runtimeClasspath\n    mainClass = 'org.maplibre.mlt.tools.SyntheticMltGenerator'\n    workingDir = project.projectDir.parentFile\n}\n"
  },
  {
    "path": "java/mlt-tools/src/main/java/org/maplibre/mlt/tools/SyntheticMltGenerator.java",
    "content": "package org.maplibre.mlt.tools;\n\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.DELTA;\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.DELTA_RLE;\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.PLAIN;\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.RLE;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.SYNTHETICS_DIR;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.array;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.c;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.c1;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.c2;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.c3;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.cfg;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.feat;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.gf;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.idFeat;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.kv;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.layer;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.line;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.line1;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.line2;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.mortonCurve;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.multi;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.p0;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.p1;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.p2;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.p3;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.ph1;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.ph2;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.ph3;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.poly;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.poly1;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.poly1h;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.poly2;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.prop;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.props;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.ring;\nimport static org.maplibre.mlt.tools.SyntheticMltUtil.write;\n\nimport java.io.IOException;\nimport java.math.BigInteger;\nimport java.nio.file.Files;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Polygon;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.unsigned.U32;\nimport org.maplibre.mlt.data.unsigned.U64;\n\npublic class SyntheticMltGenerator {\n\n  public static void main(String[] args) throws IOException {\n    if (Files.exists(SYNTHETICS_DIR)) {\n      throw new IOException(\n          \"Synthetics dir must be deleted before running `:mlt-tools:generateSyntheticMlt`: \"\n              + SYNTHETICS_DIR.toAbsolutePath());\n    }\n    Files.createDirectories(SYNTHETICS_DIR);\n\n    generatePoints();\n    generateLines();\n    generatePolygons();\n    generateMultiPoints();\n    generateMultiLineStrings();\n    generateMixed();\n    generateExtent();\n    generateIds();\n    generateProperties();\n    generateSharedDictionaries();\n    generateFpfAlignments();\n  }\n\n  private static void generatePoints() throws IOException {\n    write(\"point\", feat(p0), cfg());\n  }\n\n  private static void generateLines() throws IOException {\n    write(\"line\", feat(line1), cfg());\n\n    write(\"line_morton_curve_no_morton\", feat(line(mortonCurve)), cfg());\n    write(\"line_morton_curve_morton\", feat(line(mortonCurve)), cfg().morton());\n\n    write(\"line_zero_length\", feat(line(c(6, 6), c(6, 6))), cfg());\n  }\n\n  private static void generatePolygons() throws IOException {\n    var pol = feat(poly1);\n    write(\"poly\", pol, cfg());\n    write(\"poly_fpf\", pol, cfg().fastPFOR());\n    write(\"poly_tes\", pol, cfg().tessellate());\n    write(\"poly_fpf_tes\", pol, cfg().fastPFOR().tessellate());\n\n    var polCollinear = feat(poly(c(0, 0), c(10, 0), c(20, 0), c(0, 0)));\n    write(\"poly_collinear\", polCollinear, cfg());\n    write(\"poly_collinear_fpf\", polCollinear, cfg().fastPFOR());\n    write(\"poly_collinear_tes\", polCollinear, cfg().tessellate());\n    write(\"poly_collinear_fpf_tes\", polCollinear, cfg().fastPFOR().tessellate());\n\n    var polSelfIntersect = feat(poly(c(0, 0), c(10, 10), c(0, 10), c(10, 0), c(0, 0)));\n    write(\"poly_self_intersect\", polSelfIntersect, cfg());\n    write(\"poly_self_intersect_fpf\", polSelfIntersect, cfg().fastPFOR());\n    write(\"poly_self_intersect_tes\", polSelfIntersect, cfg().tessellate());\n    write(\"poly_self_intersect_fpf_tes\", polSelfIntersect, cfg().fastPFOR().tessellate());\n\n    // Polygon with hole\n    var polWithHole = feat(poly1h);\n    write(\"poly_hole\", polWithHole, cfg());\n    write(\"poly_hole_fpf\", polWithHole, cfg().fastPFOR());\n    write(\"poly_hole_tes\", polWithHole, cfg().tessellate());\n    write(\"poly_hole_fpf_tes\", polWithHole, cfg().fastPFOR().tessellate());\n\n    var polyHoleTouching =\n        feat(\n            poly(\n                ring(c(0, 0), c(10, 0), c(10, 10), c(0, 10), c(0, 0)),\n                ring(c(0, 0), c(2, 2), c(5, 2), c(0, 0))));\n    write(\"poly_hole_touching\", polyHoleTouching, cfg());\n    write(\"poly_hole_touching_fpf\", polyHoleTouching, cfg().fastPFOR());\n    write(\"poly_hole_touching_tes\", polyHoleTouching, cfg().tessellate());\n    write(\"poly_hole_touching_fpf_tes\", polyHoleTouching, cfg().fastPFOR().tessellate());\n\n    // MultiPolygon\n    var multiPol = feat(multi(poly1, poly2));\n    write(\"poly_multi\", multiPol, cfg());\n    write(\"poly_multi_fpf\", multiPol, cfg().fastPFOR());\n    write(\"poly_multi_tes\", multiPol, cfg().tessellate());\n    write(\"poly_multi_fpf_tes\", multiPol, cfg().fastPFOR().tessellate());\n\n    // Close the shared Morton curve into a ring to test Morton encoding for polygons.\n    var mortonRing = Arrays.copyOf(mortonCurve, mortonCurve.length + 1);\n    mortonRing[mortonCurve.length] = mortonRing[0];\n    write(\"poly_morton_ring_no_morton\", feat(poly(mortonRing)), cfg());\n    write(\"poly_morton_ring_morton\", feat(poly(mortonRing)), cfg().morton());\n\n    // Split the Morton curve into two halves (bottom 2 rows / top 2 rows of the 4x4 grid)\n    // and close each into a ring to form a MultiPolygon.\n    var half = mortonCurve.length / 2;\n    var mortonRing1 = Arrays.copyOf(mortonCurve, half + 1);\n    mortonRing1[half] = mortonRing1[0];\n    var mortonRing2src = Arrays.copyOfRange(mortonCurve, half, mortonCurve.length);\n    var mortonRing2 = Arrays.copyOf(mortonRing2src, mortonRing2src.length + 1);\n    mortonRing2[mortonRing2src.length] = mortonRing2[0];\n    write(\n        \"poly_multi_morton_ring_no_morton\",\n        feat(multi(poly(mortonRing1), poly(mortonRing2))),\n        cfg());\n    write(\n        \"poly_multi_morton_ring_morton\",\n        feat(multi(poly(mortonRing1), poly(mortonRing2))),\n        cfg().morton());\n\n    // Split the Morton curve at a different place so that the rings are different lengths,\n    // use one as the shell and one as the hole of a single and multi-polygon.\n    final var quarter = mortonCurve.length / 4;\n    mortonRing1 = Arrays.copyOf(mortonCurve, quarter + 1);\n    mortonRing1[quarter] = mortonRing1[0];\n    mortonRing2src = Arrays.copyOfRange(mortonCurve, quarter, mortonCurve.length);\n    mortonRing2 = Arrays.copyOf(mortonRing2src, mortonRing2src.length + 1);\n    mortonRing2[mortonRing2src.length] = mortonRing2[0];\n    write(\n        \"poly_morton_hole_morton\",\n        feat(poly(ring(mortonRing1), ring(mortonRing2))),\n        cfg().morton());\n    write(\n        \"poly_multi_morton_hole_morton\",\n        feat(multi(poly(ring(mortonRing1), ring(mortonRing2)))),\n        cfg().morton());\n  }\n\n  private static void generateMultiPoints() throws IOException {\n    write(\"multipoint\", feat(multi(c1, c2, c3)), cfg());\n\n    // Morton-encoded multipoint: same curve used by multiline_morton and poly_morton tests.\n    var half = mortonCurve.length / 2;\n    var mortonPts1 = Arrays.copyOf(mortonCurve, half);\n    write(\"multipoint_morton\", feat(multi(mortonPts1)), cfg().morton());\n  }\n\n  private static void generateMultiLineStrings() throws IOException {\n    write(\"multiline\", feat(multi(line1, line2)), cfg());\n\n    var half = mortonCurve.length / 2;\n    var mortonLine1 = Arrays.copyOf(mortonCurve, half);\n    var mortonLine2 = Arrays.copyOfRange(mortonCurve, half, mortonCurve.length);\n    write(\"multiline_morton\", feat(multi(line(mortonLine1), line(mortonLine2))), cfg().morton());\n  }\n\n  record GeomType(String sym, Feature feat) {}\n\n  private static void generateMixCombination(List<GeomType> current) throws IOException {\n    var name =\n        \"mix_\"\n            + current.size()\n            + \"_\"\n            + current.stream().map(GeomType::sym).collect(Collectors.joining(\"_\"));\n    var feats = current.stream().map(t -> t.feat).toArray(Feature[]::new);\n    write(layer(name, feats), cfg().geomEnc(PLAIN));\n\n    // if all geometries are of polygon type, add tessellated variant\n    if (current.stream()\n        .allMatch(\n            t -> {\n              Geometry geo = t.feat.getGeometry();\n              return geo instanceof Polygon || geo instanceof MultiPolygon;\n            })) {\n      write(layer(name + \"_tes\", feats), cfg().geomEnc(PLAIN).tessellate());\n    }\n  }\n\n  private static void generateMixedCombine(GeomType[] arr, int k, int start, List<GeomType> current)\n      throws IOException {\n    if (current.size() == k) {\n      generateMixCombination(current);\n    } else {\n      for (int i = start; i < arr.length; i++) {\n        if (i > start && arr[i] == arr[i - 1]) {\n          continue;\n        }\n        current.add(arr[i]);\n        generateMixedCombine(arr, k, i + 1, current);\n        current.removeLast();\n      }\n    }\n  }\n\n  private static void generateMixed() throws IOException {\n    GeomType[] types = {\n      new GeomType(\"pt\", feat(gf.createPoint(c(38, 29)))),\n      new GeomType(\"line\", feat(line(c(5, 38), c(12, 45), c(9, 70)))),\n      new GeomType(\"poly\", feat(poly(c(55, 5), c(58, 28), c(75, 22), c(55, 5)))),\n      new GeomType(\n          \"polyh\",\n          feat(\n              poly(\n                  ring(c(52, 35), c(14, 55), c(60, 72), c(52, 35)),\n                  ring(c(32, 50), c(36, 60), c(24, 54), c(32, 50))))),\n      new GeomType(\"mpt\", feat(multi(c(6, 25), c(21, 41), c(23, 69)))),\n      new GeomType(\n          \"mline\", feat(multi(line(c(24, 10), c(42, 18)), line(c(30, 36), c(48, 52), c(35, 62))))),\n      new GeomType(\n          \"mpoly\",\n          feat(\n              multi(\n                  poly(\n                      ring(c(7, 20), c(21, 31), c(26, 9), c(7, 20)),\n                      ring(c(15, 20), c(20, 15), c(18, 25), c(15, 20))),\n                  poly(c(69, 57), c(71, 66), c(73, 64), c(69, 57))))),\n    };\n\n    for (int k = 2; k <= types.length; k++) {\n      generateMixedCombine(types, k, 0, new ArrayList<>());\n    }\n    for (var a : types) {\n      // Create A-A variants\n      generateMixCombination(List.of(a, a));\n      for (var b : types) {\n        if (!a.sym.equals(b.sym)) {\n          // Create A-B-A variants\n          generateMixCombination(List.of(a, b, a));\n        }\n      }\n    }\n  }\n\n  private static void generateExtent() throws IOException {\n    int[] extents = {512, 4096, 131072, 1073741824};\n    for (int e : extents) {\n      write(layer(\"extent_\" + e, e, feat(line(c(0, 0), c(e - 1, e - 1)))), cfg());\n      write(layer(\"extent_buf_\" + e, e, feat(line(c(-42, -42), c(e + 42, e + 42)))), cfg());\n    }\n  }\n\n  private static void generateIds() throws IOException {\n    write(\"id\", idFeat(100), cfg().ids());\n    write(\"id_min\", idFeat(0), cfg().ids());\n    // FIXME: serialises as -1\n    // write(\"id_max\", idFeat(0xFFFFFFFF), cfg().ids());\n    write(\"id64\", idFeat(9_234_567_890L), cfg().ids());\n    // FIXME: writes as Id32 instead of Id64\n    // write(\"id64_max\", idFeat(0xFFFFFFFFFFFFFFFFL), cfg().ids());\n\n    var ids32 = array(idFeat(103), idFeat(103), idFeat(103), idFeat(103));\n    write(layer(\"ids\", ids32), cfg().ids());\n    write(layer(\"ids_delta\", ids32), cfg(DELTA).ids());\n    write(layer(\"ids_rle\", ids32), cfg(RLE).ids());\n    write(layer(\"ids_delta_rle\", ids32), cfg(DELTA_RLE).ids());\n\n    var ids64 =\n        array(\n            idFeat(9_234_567_890L),\n            idFeat(9_234_567_890L),\n            idFeat(9_234_567_890L),\n            idFeat(9_234_567_890L));\n    write(layer(\"ids64\", ids64), cfg().ids());\n    write(layer(\"ids64_delta\", ids64), cfg(DELTA).ids());\n    write(layer(\"ids64_rle\", ids64), cfg(RLE).ids());\n    write(layer(\"ids64_delta_rle\", ids64), cfg(DELTA_RLE).ids());\n\n    var optIds = array(idFeat(100), idFeat(101), idFeat(), idFeat(105), idFeat(106));\n    write(layer(\"ids_opt\", optIds), cfg().ids());\n    write(layer(\"ids_opt_delta\", optIds), cfg(DELTA).ids());\n\n    var optIds64 = array(idFeat(), idFeat(9_234_567_890L), idFeat(101), idFeat(105), idFeat(106));\n    write(layer(\"ids64_opt\", optIds64), cfg().ids());\n    write(layer(\"ids64_opt_delta\", optIds64), cfg(DELTA).ids());\n\n    // Java does not generate this as an ID64 if none of them are above the U32::Max threshold\n    // FIXME: serialises as i64\n    // var ids64MinMax = array(idFeat(0L), idFeat(0xFFFFFFFFFFFFFFFFL), idFeat(0L),\n    // idFeat(0xFFFFFFFFFFFFFFFFL));\n    // write(layer(\"ids64_minmax\", ids64MinMax), cfg().ids());\n    // write(layer(\"ids64_minmax_delta\", ids64MinMax), cfg(DELTA).ids());\n  }\n\n  @SuppressWarnings(\"cast\")\n  private static void generateProperties() throws IOException {\n    write(\"prop_empty_name\", feat(p0, prop(\"\", true)), cfg());\n    write(\"prop_special_name\", feat(p0, prop(\"hello\\u0000 world\\n\", true)), cfg());\n\n    write(\"prop_bool\", feat(p0, prop(\"val\", true)), cfg());\n    write(\"prop_bool_false\", feat(p0, prop(\"val\", false)), cfg());\n    write(layer(\"prop_bool_true_null\", feat(p0, prop(\"val\", true)), feat(p0)), cfg());\n    write(layer(\"prop_bool_null_true\", feat(p0), feat(p0, prop(\"val\", true))), cfg());\n    write(layer(\"prop_bool_false_null\", feat(p0, prop(\"val\", false)), feat(p0)), cfg());\n    write(layer(\"prop_bool_null_false\", feat(p0), feat(p0, prop(\"val\", false))), cfg());\n\n    // FIXME: needs support in the Java decoder + encoder\n    // write(\"prop_i8\", feat(p0, prop(\"val\", (byte) 42)), cfg());\n    // write(\"prop_i8_neg\", feat(p0, prop(\"val\", (byte) -42)), cfg());\n    // write(\"prop_i8_min\", feat(p0, prop(\"val\", Byte.MIN_VALUE)), cfg());\n    // write(\"prop_i8_max\", feat(p0, prop(\"val\", Byte.MAX_VALUE)), cfg());\n    // write(layer(\"prop_i8_val_null\", feat(p0, prop(\"val\", (byte) 42)), feat(p0)), cfg());\n    // write(layer(\"prop_i8_null_val\", feat(p0), feat(p0, prop(\"val\", (byte) 42))), cfg());\n\n    // write(\"prop_u8\", feat(p0, prop(\"tinynum\", U8.of(100))), cfg());\n    // write(\"prop_u8_min\", feat(p0, prop(\"tinynum\", U8.of(0))), cfg());\n    // write(\"prop_u8_max\", feat(p0, prop(\"tinynum\", U8.of(255))), cfg());\n    // write(layer(\"prop_u8_val_null\", feat(p0, prop(\"val\", U8.of(100))), feat(p0)), cfg());\n    // write(layer(\"prop_u8_null_val\", feat(p0), feat(p0, prop(\"val\", U8.of(100)))), cfg());\n\n    // write(\"prop_i16\", feat(p0, prop(\"val\", (short) 42)), cfg());\n    // write(\"prop_i16_neg\", feat(p0, prop(\"val\", (short) -42)), cfg());\n    // write(\"prop_i16_min\", feat(p0, prop(\"val\", Short.MIN_VALUE)), cfg());\n    // write(\"prop_i16_max\", feat(p0, prop(\"val\", Short.MAX_VALUE)), cfg());\n    // write(layer(\"prop_i16_val_null\", feat(p0, prop(\"val\", (short) 42)), feat(p0)), cfg());\n    // write(layer(\"prop_i16_null_val\", feat(p0), feat(p0, prop(\"val\", (short) 42))), cfg());\n\n    write(\"prop_i32\", feat(p0, prop(\"val\", (int) 42)), cfg());\n    write(\"prop_i32_neg\", feat(p0, prop(\"val\", (int) -42)), cfg());\n    write(\"prop_i32_min\", feat(p0, prop(\"val\", Integer.MIN_VALUE)), cfg());\n    write(\"prop_i32_max\", feat(p0, prop(\"val\", Integer.MAX_VALUE)), cfg());\n    write(layer(\"prop_i32_val_null\", feat(p0, prop(\"val\", (int) 42)), feat(p0)), cfg());\n    write(layer(\"prop_i32_null_val\", feat(p0), feat(p0, prop(\"val\", (int) 42))), cfg());\n\n    write(\"prop_u32\", feat(p0, prop(\"val\", U32.of(42L))), cfg());\n    write(\"prop_u32_min\", feat(p0, prop(\"val\", U32.of(0L))), cfg());\n    write(\"prop_u32_max\", feat(p0, prop(\"val\", U32.of(0xFFFFFFFFL))), cfg());\n    write(layer(\"prop_u32_val_null\", feat(p0, prop(\"val\", U32.of(42L))), feat(p0)), cfg());\n    write(layer(\"prop_u32_null_val\", feat(p0), feat(p0, prop(\"val\", U32.of(42L)))), cfg());\n\n    long i64_value = 9_876_543_210L;\n    write(\"prop_i64\", feat(p0, prop(\"val\", i64_value)), cfg());\n    write(\"prop_i64_neg\", feat(p0, prop(\"val\", (long) -9_876_543_210L)), cfg());\n    write(\"prop_i64_min\", feat(p0, prop(\"val\", Long.MIN_VALUE)), cfg());\n    write(\"prop_i64_max\", feat(p0, prop(\"val\", Long.MAX_VALUE)), cfg());\n    write(layer(\"prop_i64_val_null\", feat(p0, prop(\"val\", i64_value)), feat(p0)), cfg());\n    write(layer(\"prop_i64_null_val\", feat(p0), feat(p0, prop(\"val\", i64_value))), cfg());\n\n    U64 u64_value = U64.of(BigInteger.valueOf(1234567890123456789L));\n    U64 u64_max = U64.of(new BigInteger(\"18446744073709551615\"));\n    write(\"prop_u64\", feat(p0, prop(\"bignum\", u64_value)), cfg());\n    write(\"prop_u64_min\", feat(p0, prop(\"bignum\", U64.of(BigInteger.ZERO))), cfg());\n    write(\"prop_u64_max\", feat(p0, prop(\"bignum\", u64_max)), cfg());\n    write(layer(\"prop_u64_val_null\", feat(p0, prop(\"val\", u64_value)), feat(p0)), cfg());\n    write(layer(\"prop_u64_null_val\", feat(p0), feat(p0, prop(\"val\", u64_value))), cfg());\n\n    write(\"prop_f32\", feat(p0, prop(\"val\", (float) 3.14f)), cfg());\n    write(\"prop_f32_neg_inf\", feat(p0, prop(\"val\", Float.NEGATIVE_INFINITY)), cfg());\n    write(\"prop_f32_min_norm\", feat(p0, prop(\"val\", Float.MIN_NORMAL)), cfg());\n    write(\"prop_f32_min_val\", feat(p0, prop(\"val\", Float.MIN_VALUE)), cfg());\n    write(\"prop_f32_neg_zero\", feat(p0, prop(\"val\", (float) -0.0f)), cfg());\n    write(\"prop_f32_zero\", feat(p0, prop(\"val\", (float) 0.0f)), cfg());\n    write(\"prop_f32_max\", feat(p0, prop(\"val\", Float.MAX_VALUE)), cfg());\n    write(\"prop_f32_pos_inf\", feat(p0, prop(\"val\", Float.POSITIVE_INFINITY)), cfg());\n    write(\"prop_f32_nan\", feat(p0, prop(\"val\", Float.NaN)), cfg());\n    write(layer(\"prop_f32_val_null\", feat(p0, prop(\"val\", 3.14f)), feat(p0)), cfg());\n    write(layer(\"prop_f32_null_val\", feat(p0), feat(p0, prop(\"val\", 3.14f))), cfg());\n\n    write(\"prop_f64\", feat(p0, prop(\"val\", (Double) Math.PI)), cfg());\n    write(\"prop_f64_neg_inf\", feat(p0, prop(\"val\", Double.NEGATIVE_INFINITY)), cfg());\n    write(\"prop_f64_min_norm\", feat(p0, prop(\"val\", Double.MIN_NORMAL)), cfg());\n    write(\"prop_f64_min_val\", feat(p0, prop(\"val\", Double.MIN_VALUE)), cfg());\n    write(\"prop_f64_neg_zero\", feat(p0, prop(\"val\", (double) -0.0)), cfg());\n    write(\"prop_f64_zero\", feat(p0, prop(\"val\", (double) 0.0)), cfg());\n    write(\"prop_f64_max\", feat(p0, prop(\"val\", Double.MAX_VALUE)), cfg());\n    write(\"prop_f64_pos_inf\", feat(p0, prop(\"val\", Double.POSITIVE_INFINITY)), cfg());\n    write(\"prop_f64_nan\", feat(p0, prop(\"val\", Double.NaN)), cfg());\n    write(layer(\"prop_f64_val_null\", feat(p0, prop(\"val\", (Double) Math.PI)), feat(p0)), cfg());\n    write(layer(\"prop_f64_null_val\", feat(p0), feat(p0, prop(\"val\", (Double) Math.PI))), cfg());\n\n    write(\"prop_str_empty\", feat(p0, prop(\"val\", \"\")), cfg());\n    write(\"prop_str_ascii\", feat(p0, prop(\"val\", \"42\")), cfg());\n    write(\"prop_str_escape\", feat(p0, prop(\"val\", \"Line1\\n\\t\\\"quoted\\\"\\\\path\")), cfg());\n    write(\"prop_str_unicode\", feat(p0, prop(\"val\", \"München 📍 cafe\\u0301\")), cfg());\n    write(\"prop_str_special\", feat(p0, prop(\"val\", \"hello\\u0000 world\\n\")), cfg());\n    write(layer(\"prop_str_val_null\", feat(p0, prop(\"val\", \"42\")), feat(p0)), cfg());\n    write(layer(\"prop_str_null_val\", feat(p0), feat(p0, prop(\"val\", \"42\"))), cfg());\n    write(layer(\"prop_str_val_empty\", feat(p0, prop(\"val\", \"\")), feat(p0)), cfg());\n    write(layer(\"prop_str_empty_val\", feat(p0), feat(p0, prop(\"val\", \"\"))), cfg());\n\n    // Multiple properties - single feature demonstrating multiple property types\n    write(\n        \"props_mixed\",\n        feat(\n            p0,\n            props(\n                kv(\"name\", \"Test Point\"),\n                kv(\"active\", true),\n                // FIXME: needs support in the Java decoder + encoder\n                // kv(\"tiny-count\", (byte) 42),\n                // FIXME: needs support in the decoder + encoder\n                // kv(\"tiny\", U8.of(100)),\n                kv(\"count\", 42),\n                kv(\"medium\", U32.of(100)),\n                kv(\"bignum\", 42L), // FIXME: this is encoded as i32\n                kv(\"biggest\", U64.of(BigInteger.ZERO)),\n                kv(\"temp\", 25.5f),\n                kv(\"precision\", 0.123456789))),\n        cfg());\n\n    // FIXME: needs support in the decoder + encoder\n    // var feat_u8s =\n    //    array(\n    //        feat(p1, prop(\"val\", U8.of(100))),\n    //        feat(p2, prop(\"val\", U8.of(100))),\n    //        feat(p3, prop(\"val\", U8.of(100))),\n    //        feat(p4, prop(\"val\", U8.of(100))));\n    // write(layer(\"props_u8\", feat_u8s), cfg());\n    // write(layer(\"props_u8_delta\", feat_u8s), cfg(DELTA));\n    // write(layer(\"props_u8_rle\", feat_u8s), cfg(RLE));\n    // write(layer(\"props_u8_delta_rle\", feat_u8s), cfg(DELTA_RLE));\n\n    var feat_ints =\n        array(\n            feat(p0, prop(\"val\", 42)),\n            feat(p1, prop(\"val\", 42)),\n            feat(p2, prop(\"val\", 42)),\n            feat(p3, prop(\"val\", 42)));\n    write(layer(\"props_i32\", feat_ints), cfg());\n    write(layer(\"props_i32_delta\", feat_ints), cfg(DELTA));\n    write(layer(\"props_i32_rle\", feat_ints), cfg(RLE));\n    write(layer(\"props_i32_delta_rle\", feat_ints), cfg(DELTA_RLE));\n\n    var feat_u32s =\n        array(\n            feat(p0, prop(\"val\", U32.of(9_000))),\n            feat(p1, prop(\"val\", U32.of(9_000))),\n            feat(p2, prop(\"val\", U32.of(9_000))),\n            feat(p3, prop(\"val\", U32.of(9_000))));\n    write(layer(\"props_u32\", feat_u32s), cfg());\n    write(layer(\"props_u32_delta\", feat_u32s), cfg(DELTA));\n    write(layer(\"props_u32_rle\", feat_u32s), cfg(RLE));\n    write(layer(\"props_u32_delta_rle\", feat_u32s), cfg(DELTA_RLE));\n\n    // The actual frame size is 256, so cover multiples of its half\n    for (var multiplier : new int[] {1, 2, 3, 4}) {\n      for (var offset : new int[] {-1, 0, 1}) {\n        var features = new Feature[128 * multiplier + offset];\n        for (var i = 0; i < features.length; i++) {\n          // Sequence 0,1,2, 0,1,2, 0,1,2, 0,1,2, ...\n          features[i] = feat(p0, prop(\"val\", U32.of(i % 3)));\n        }\n        write(layer(\"props_u32_fpf_\" + features.length, features), cfg().fastPFOR());\n      }\n    }\n\n    var feat_u64s =\n        array(\n            feat(p0, prop(\"val\", U64.of(BigInteger.valueOf(9_000L)))),\n            feat(p1, prop(\"val\", U64.of(BigInteger.valueOf(9_000L)))),\n            feat(p2, prop(\"val\", U64.of(BigInteger.valueOf(9_000L)))),\n            feat(p3, prop(\"val\", U64.of(BigInteger.valueOf(9_000L)))));\n    write(layer(\"props_u64\", feat_u64s), cfg());\n    write(layer(\"props_u64_delta\", feat_u64s), cfg(DELTA));\n    write(layer(\"props_u64_rle\", feat_u64s), cfg(RLE));\n    write(layer(\"props_u64_delta_rle\", feat_u64s), cfg(DELTA_RLE));\n\n    // i64 multi-feature: exercises NONE/DELTA/RLE/DELTA_RLE logical techniques for signed 64-bit.\n    // The value must exceed U32::MAX so the encoder uses a 64-bit column.\n    var feat_i64s =\n        array(\n            feat(p0, prop(\"val\", 9_876_543_210L)),\n            feat(p1, prop(\"val\", 9_876_543_210L)),\n            feat(p2, prop(\"val\", 9_876_543_210L)),\n            feat(p3, prop(\"val\", 9_876_543_210L)));\n    write(layer(\"props_i64\", feat_i64s), cfg());\n    write(layer(\"props_i64_delta\", feat_i64s), cfg(DELTA));\n    write(layer(\"props_i64_rle\", feat_i64s), cfg(RLE));\n    write(layer(\"props_i64_delta_rle\", feat_i64s), cfg(DELTA_RLE));\n\n    var feat_str =\n        array(\n            feat(p1, prop(\"val\", \"residential_zone_north_sector_1\")),\n            feat(p2, prop(\"val\", \"commercial_zone_south_sector_2\")),\n            feat(p3, prop(\"val\", \"industrial_zone_east_sector_3\")),\n            feat(ph1, prop(\"val\", \"park_zone_west_sector_4\")),\n            feat(ph2, prop(\"val\", \"water_zone_north_sector_5\")),\n            feat(ph3, prop(\"val\", \"residential_zone_south_sector_6\")));\n    write(layer(\"props_str\", feat_str), cfg());\n    write(layer(\"props_str_fsst\", feat_str), cfg().fsst());\n\n    // 30 because otherwise fsst is skipped\n    var val = \"A\".repeat(30);\n\n    // If there are many identical strings in the same column,\n    // an offset directory is used to share them\n    // Because the directory we are indexing into has lengths assosciated with it and the offsets\n    // are indexes,\n    // we cannot do overlap-optimization where one ABBA contains BB -> only ABBA would be in the\n    // dict.\n    // -> ABBABB would need to be in the dict\n    var feat_two_str_eq = array(feat(p1, prop(\"val\", val)), feat(p2, prop(\"val\", val)));\n    write(layer(\"props_offset_str\", feat_two_str_eq), cfg());\n    write(layer(\"props_offset_str_fsst\", feat_two_str_eq), cfg().fsst());\n  }\n\n  /**\n   * Generates FastPFOR tile in 8 variants, with the layer name padded by 0-7 extra bytes, so that\n   * the FPF stream starts at every possible byte offset mod 8.\n   */\n  private static void generateFpfAlignments() throws IOException {\n    var features = new Feature[128];\n    for (var i = 0; i < features.length; i++) {\n      features[i] = feat(p0, prop(\"v\", U32.of(i % 3)));\n    }\n    for (var pad = 0; pad < 8; pad++) {\n      write(\n          \"fpf_align_\" + (pad + 1),\n          List.of(new Layer(\"a\".repeat(pad + 1), Arrays.asList(features), 80)),\n          cfg().fastPFOR());\n    }\n  }\n\n  private static void generateSharedDictionaries() throws IOException {\n    // 30 because otherwise fsst is skipped\n    var val = \"A\".repeat(30);\n    var feat_names = array(feat(p0, props(kv(\"name:en\", val), kv(\"name:de\", val))));\n\n    write(layer(\"props_no_shared_dict\", feat_names), cfg());\n    write(layer(\"props_shared_dict\", feat_names), cfg().sharedDictPrefix(\"name\", \":\"));\n    write(layer(\"props_shared_dict_fsst\", feat_names), cfg().sharedDictPrefix(\"name\", \":\").fsst());\n\n    var feat_one_child = array(feat(p0, props(kv(\"name:en\", val), kv(\"place\", val))));\n    write(\n        layer(\"props_shared_dict_one_child\", feat_one_child), cfg().sharedDictPrefix(\"name\", \":\"));\n    write(\n        layer(\"props_shared_dict_one_child_fsst\", feat_one_child),\n        cfg().sharedDictPrefix(\"name\", \":\").fsst());\n\n    var feat_distinct_keys_same_value = array(feat(p0, props(kv(\"a\", val), kv(\"b\", val))));\n    write(\n        layer(\"props_shared_dict_no_struct_name\", feat_distinct_keys_same_value),\n        cfg().sharedDictPrefix(\"\", \"\"));\n    write(\n        layer(\"props_shared_dict_no_struct_name_fsst\", feat_distinct_keys_same_value),\n        cfg().sharedDictPrefix(\"\", \"\").fsst());\n\n    var feat_names_eq_val_eq = array(feat(p0, props(kv(\"a\", val), kv(\"a\", val))));\n    write(\n        layer(\"props_shared_dict_no_child_name\", feat_names_eq_val_eq),\n        cfg().sharedDictPrefix(\"a\", \"\"));\n    write(\n        layer(\"props_shared_dict_no_child_name_fsst\", feat_names_eq_val_eq),\n        cfg().sharedDictPrefix(\"a\", \"\").fsst());\n\n    // Two separate shared dicts with the same \"name\" prefix, split by explicit column groups\n    var feat_names_4 =\n        array(\n            feat(\n                p0,\n                props(\n                    kv(\"name:de\", val),\n                    kv(\"name_en\", val),\n                    kv(\"name_fr\", val),\n                    kv(\"name:he\", val))));\n    write(\n        layer(\"props_shared_dict_2_same_prefix\", feat_names_4),\n        cfg()\n            .sharedDictColumnGroups(\n                List.of(List.of(\"name:de\", \"name_en\"), List.of(\"name_fr\", \"name:he\"))));\n  }\n}\n"
  },
  {
    "path": "java/mlt-tools/src/main/java/org/maplibre/mlt/tools/SyntheticMltUtil.java",
    "content": "package org.maplibre.mlt.tools;\n\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.AUTO;\nimport static org.maplibre.mlt.converter.ConversionConfig.IntegerEncodingOption.PLAIN;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardOpenOption;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.regex.Pattern;\nimport org.locationtech.jts.geom.Coordinate;\nimport org.locationtech.jts.geom.Geometry;\nimport org.locationtech.jts.geom.GeometryFactory;\nimport org.locationtech.jts.geom.LineString;\nimport org.locationtech.jts.geom.LinearRing;\nimport org.locationtech.jts.geom.MultiLineString;\nimport org.locationtech.jts.geom.MultiPoint;\nimport org.locationtech.jts.geom.MultiPolygon;\nimport org.locationtech.jts.geom.Point;\nimport org.locationtech.jts.geom.Polygon;\nimport org.locationtech.jts.geom.PrecisionModel;\nimport org.maplibre.mlt.converter.ColumnMapping;\nimport org.maplibre.mlt.converter.ColumnMappingConfig;\nimport org.maplibre.mlt.converter.ConversionConfig;\nimport org.maplibre.mlt.converter.FeatureTableOptimizations;\nimport org.maplibre.mlt.converter.MltConverter;\nimport org.maplibre.mlt.data.Feature;\nimport org.maplibre.mlt.data.Layer;\nimport org.maplibre.mlt.data.MLTFeature;\nimport org.maplibre.mlt.data.MapboxVectorTile;\nimport org.maplibre.mlt.decoder.MltDecoder;\nimport org.maplibre.mlt.json.Json;\n\n/** Utility helpers for synthetic MLT generation. */\nclass SyntheticMltUtil {\n\n  static final Path SYNTHETICS_DIR = Paths.get(\"../test/synthetic/0x01\");\n\n  // Using common coordinates everywhere to make sure generated MLT files are very similar,\n  // ensuring we observe difference in encoding rather than geometry variations.\n  // Use SRID 4326 for visualization - all coords are in positive longitude/latitude\n  // range, but in reality the coords are in SRID=0 tile space.\n  // Use coordinates X in 0..180, Y in 0..85 space to match lon/lat ranges and help QGIS vis.\n  // Try to keep all values unique to simplify validation and debugging.\n  // Use tiny tile extent 80 for most geometry tests to focus on encoding correctness.\n  static final GeometryFactory gf = new GeometryFactory(new PrecisionModel(), 4326);\n  static final Coordinate c0 = c(13, 42);\n  // triangle 1, clockwise winding, X ends in 1, Y ends in 2\n  static final Coordinate c1 = c(11, 52);\n  static final Coordinate c2 = c(71, 72);\n  static final Coordinate c3 = c(61, 22);\n  // triangle 2, clockwise winding, X ends in 3, Y ends in 4\n  static final Coordinate c21 = c(23, 34);\n  static final Coordinate c22 = c(73, 4);\n  static final Coordinate c23 = c(13, 24);\n  // hole in triangle 1 with counter-clockwise winding\n  static final Coordinate h1 = c(65, 66);\n  static final Coordinate h2 = c(35, 56);\n  static final Coordinate h3 = c(55, 36);\n\n  static final Point p0 = gf.createPoint(c0);\n  static final Point p1 = gf.createPoint(c1);\n  static final Point p2 = gf.createPoint(c2);\n  static final Point p3 = gf.createPoint(c3);\n  // holes as points with same coordinates as the hole vertices\n  static final Point ph1 = gf.createPoint(h1);\n  static final Point ph2 = gf.createPoint(h2);\n  static final Point ph3 = gf.createPoint(h3);\n\n  static final LineString line1 = line(c1, c2, c3);\n  static final LineString line2 = line(c21, c22, c23);\n  static final Polygon poly1 = poly(c1, c2, c3, c1);\n  static final Polygon poly2 = poly(c21, c22, c23, c21);\n  static final Polygon poly1h = poly(ring(c1, c2, c3, c1), ring(h1, h2, h3, h1));\n  // 4x4 complete Morton block: 16 points decoded from Z-order (even bits → x, odd bits → y).\n  // Shared by line_morton and poly_morton so both test the same coordinate pattern.\n  static final Coordinate[] mortonCurve = buildMortonCurve(16, 8, 4);\n\n  /** Builder subclass with no-argument shorthand methods for common flags. */\n  static class Cfg extends ConversionConfig.ConfigBuilder {\n    public Cfg ids() {\n      super.includeIds(true);\n      return this;\n    }\n\n    public Cfg fastPFOR() {\n      super.useFastPFOR(true);\n      return this;\n    }\n\n    public Cfg fsst() {\n      super.useFSST(true);\n      return this;\n    }\n\n    public Cfg geomEnc(ConversionConfig.IntegerEncodingOption encoding) {\n      super.geometryEncodingOption(encoding);\n      return this;\n    }\n\n    public Cfg coercePropValues() {\n      super.typeMismatchPolicy(ConversionConfig.TypeMismatchPolicy.COERCE);\n      return this;\n    }\n\n    public Cfg tessellate() {\n      super.preTessellatePolygons(true);\n      return this;\n    }\n\n    public Cfg morton() {\n      super.useMortonEncoding(true);\n      return this;\n    }\n\n    public Cfg filterInvert() {\n      super.layerFilterInvert(true);\n      return this;\n    }\n\n    /**\n     * Enable shared dictionary encoding for properties with names starting with the given prefix\n     * string.\n     */\n    public Cfg sharedDictPrefix(String prefix, String delimiter) {\n      var mapping = new ColumnMapping(prefix, delimiter, true);\n      var layerOpt = new FeatureTableOptimizations(false, false, List.of(mapping));\n\n      var optimizationsMap = new HashMap<String, FeatureTableOptimizations>();\n      optimizationsMap.put(\"layer1\", layerOpt);\n      super.optimizations(optimizationsMap);\n      return this;\n    }\n\n    /**\n     * Enable shared dictionary encoding using explicit groups of column names. Each group becomes\n     * its own shared dictionary.\n     */\n    public Cfg sharedDictColumnGroups(List<List<String>> columnGroups) {\n      var mappings = columnGroups.stream().map(cols -> new ColumnMapping(cols, true)).toList();\n      var layerOpt = new FeatureTableOptimizations(false, false, mappings);\n\n      var optimizationsMap = new HashMap<String, FeatureTableOptimizations>();\n      optimizationsMap.put(\"layer1\", layerOpt);\n      this.optimizations(optimizationsMap);\n      return this;\n    }\n  }\n\n  static Cfg cfg() {\n    return cfg(PLAIN);\n  }\n\n  static Cfg cfg(ConversionConfig.IntegerEncodingOption encoding) {\n    var c = new Cfg();\n    c.includeIds(false);\n    c.useFastPFOR(false);\n    c.useFSST(false);\n    c.typeMismatchPolicy(ConversionConfig.TypeMismatchPolicy.FAIL);\n    // c.optimizations(null); // Map<String, FeatureTableOptimizations>\n    c.preTessellatePolygons(false);\n    c.useMortonEncoding(false);\n    // ALWAYS use outlines for synthetic tests. When tessellation is enabled, a polygon outline\n    // also needs to be stored in the tile, or else only triangles would be stored and the\n    // Java decoder (and other decoders) do not currently support triangle meshes without\n    // corresponding outline polygons. Using \"ALL\" here ensures outlines are present for both\n    // tessellated and non-tessellated cases in all synthetic test tiles.\n    c.outlineFeatureTableNames(List.of(\"ALL\"));\n    // c.layerFilterPattern(null); // Pattern\n    c.layerFilterInvert(false);\n    c.integerEncodingOption(encoding);\n    c.geometryEncodingOption(AUTO);\n    return c;\n  }\n\n  @SafeVarargs\n  @SuppressWarnings(\"varargs\")\n  static <T> T[] array(T... elements) {\n    return elements;\n  }\n\n  static Coordinate c(int x, int y) {\n    return new Coordinate(x, y);\n  }\n\n  /** De-interleave Z-order index bits into x (even bits) and y (odd bits) coordinates. */\n  static Coordinate[] buildMortonCurve(int numPoints, int scale, int mortonBits) {\n    var curve = new Coordinate[numPoints];\n    for (var i = 0; i < numPoints; i++) {\n      var x = 0;\n      var y = 0;\n      for (var b = 0; b < mortonBits; b++) {\n        x |= ((i >> (2 * b)) & 1) << b;\n        y |= ((i >> (2 * b + 1)) & 1) << b;\n      }\n      curve[i] = c(x * scale, y * scale);\n    }\n    return curve;\n  }\n\n  static LineString line(Coordinate... coords) {\n    return gf.createLineString(coords);\n  }\n\n  static LinearRing ring(Coordinate... coords) {\n    return gf.createLinearRing(coords);\n  }\n\n  static Polygon poly(Coordinate... coords) {\n    return gf.createPolygon(coords);\n  }\n\n  static Polygon poly(LinearRing shell, LinearRing... holes) {\n    return gf.createPolygon(shell, holes);\n  }\n\n  static MultiPoint multi(Coordinate... coords) {\n    var pts = Arrays.stream(coords).map(t -> gf.createPoint(t)).toArray(Point[]::new);\n    return gf.createMultiPoint(pts);\n  }\n\n  static MultiPolygon multi(Polygon... polys) {\n    return gf.createMultiPolygon(polys);\n  }\n\n  static MultiLineString multi(LineString... lines) {\n    return gf.createMultiLineString(lines);\n  }\n\n  record KeyVal(String key, Object value) {}\n\n  static KeyVal kv(String key, Object value) {\n    return new KeyVal(key, value);\n  }\n\n  static Map<String, Object> prop(String key, Object value) {\n    return Map.of(key, value);\n  }\n\n  static Map<String, Object> props(KeyVal... keyValues) {\n    var map = new java.util.HashMap<String, Object>();\n    for (var kv : keyValues) {\n      map.put(kv.key, kv.value);\n    }\n    return map;\n  }\n\n  static Feature feat(Geometry geom) {\n    return MLTFeature.builder().index(0).geometry(geom).build();\n  }\n\n  static MLTFeature feat(Geometry geom, Map<String, Object> props) {\n    return MLTFeature.builder().index(0).geometry(geom).rawProperties(props).build();\n  }\n\n  /** for testing IDs - always use the same geometry */\n  static Feature idFeat(long id) {\n    return MLTFeature.builder().index(0).id(id).geometry(p0).build();\n  }\n\n  /** for testing IDs - simulate missing ID */\n  static Feature idFeat() {\n    return feat(p0);\n  }\n\n  static Layer layer(String name, Feature... features) {\n    return new Layer(name, Arrays.asList(features), 80);\n  }\n\n  static Layer layer(String name, int extent, Feature... features) {\n    return new Layer(name, Arrays.asList(features), extent);\n  }\n\n  static void write(String name, Feature feat, ConversionConfig.ConfigBuilder cfg)\n      throws IOException {\n    write(layer(name, feat), cfg);\n  }\n\n  static void write(Layer layer, ConversionConfig.ConfigBuilder cfg) throws IOException {\n    // layer names should be identical to reduce variability in generated MLT files\n    // and ensure we observe differences in encoding rather than layer name variations\n    write(layer.name(), List.of(new Layer(\"layer1\", layer.features(), layer.tileExtent())), cfg);\n  }\n\n  static void write(String fileName, List<Layer> layers, ConversionConfig.ConfigBuilder cfg)\n      throws IOException {\n    try {\n      System.out.println(\"Generating: \" + fileName);\n      var config = cfg.build();\n      var tile = new MapboxVectorTile(layers);\n      final var columnMappings = buildColumnMappings(config);\n      var metadata = MltConverter.createTilesetMetadata(tile, columnMappings, config.includeIds());\n      var mlt = MltConverter.encode(tile, metadata, config, null);\n      Files.write(SYNTHETICS_DIR.resolve(fileName + \".mlt\"), mlt, StandardOpenOption.CREATE_NEW);\n      final String json = Json.toGeoJson(MltDecoder.decodeMlTile(mlt), true) + \"\\n\";\n      Files.writeString(\n          SYNTHETICS_DIR.resolve(fileName + \".json\"), json, StandardOpenOption.CREATE_NEW);\n    } catch (Exception e) {\n      throw new IOException(\"Error writing MLT file \" + fileName, e);\n    }\n  }\n\n  private static ColumnMappingConfig buildColumnMappings(ConversionConfig config) {\n    final var columnMappings = new ColumnMappingConfig();\n    if (config.optimizations() != null && !config.optimizations().isEmpty()) {\n      var allColumnMappings =\n          config.optimizations().values().stream()\n              .flatMap(opt -> opt.columnMappings().stream())\n              .toList();\n      if (!allColumnMappings.isEmpty()) {\n        columnMappings.put(Pattern.compile(\".*\"), allColumnMappings);\n      }\n    }\n    return columnMappings;\n  }\n}\n"
  },
  {
    "path": "java/mod.just",
    "content": "ci_mode := if env('CI', '') != '' {'1'} else {''}\njust := quote(just_executable())\n\n_default: (just '--list' 'java')\n\n[private]\njust *args:\n    {{just}} {{args}}\n\n# Quick compile check\ncheck:\n    ./gradlew check\n\n# Run all CI steps: lint, test, cli tests, coverage, synthetic MLT check\nci-test: env-info lint test test-cli test-publish ci-check-synthetic-mlts\n    {{just}} assert-git-is-clean\n\n# Verify synthetic MLT files are up to date\nci-check-synthetic-mlts:\n    {{just}} assert-git-is-clean\n    {{just}} java::generate-synthetic-mlts\n    {{just}} assert-git-is-clean\n    @echo \"Synthetic MLT check complete.\"\n\n# Print environment info\nenv-info:\n    @echo \"Running {{if ci_mode == '1' {'in CI mode'} else {'in dev mode'} }} on {{os()}} / {{arch()}}\"\n    ./gradlew --version\n\n# Reformat code\nfmt:\n    ./gradlew spotlessApply\n\n# Clean build artifacts\nclean:\n    ./gradlew clean\n\n# Build Java encoder and generate .mlt files for all fixtures\ngenerate-expected-mlt: (just 'cargo-install' 'fd' 'fd-find')\n    ./gradlew cli\n    fd . ../test/fixtures --no-ignore --extension pbf --extension mvt -x {{just}} java::_generate-one-expected-mlt\n\n# Build Java encoder and run it to generate snippets for all .pbf files\ngen-snippets: (just 'cargo-install' 'fd' 'fd-find')\n    #!/usr/bin/env bash\n    set -euo pipefail\n    ./gradlew cli\n    fd . ../test/fixtures -e pbf -x java -jar mlt-cli/build/libs/encode.jar --mvt {} \\\n        --mlt \"$(tmp='{.}'; echo ${tmp/\\/fixtures\\///expected/}).mvt\" \\\n        --rawstreams --tessellate --outlines ALL || echo \"### Some snippets failed to generate ###\"\n\n# Build Java encoder and run it to generate test files\ngen-tests: (just 'cargo-install' 'fd' 'fd-find')\n    #!/usr/bin/env bash\n    set -euo pipefail\n    ./gradlew cli\n    fd . ../test/fixtures -e pbf -x bash -c 'java -jar mlt-cli/build/libs/encode.jar --mvt {} \\\n        --mlt \"$(tmp={.}; echo ${tmp/fixtures/expected/tag0x01/}).mlt\" \\\n        --outlines \"\\*\" --tessellate --coerce-mismatch --verbose || echo \"### Some snippets failed to generate ###\"'\n\n# Generate synthetic .mlt files and ensure there are no duplicates\ngenerate-synthetic-mlts:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    rm -rf ../test/synthetic/0x01\n    ./gradlew :mlt-tools:generateSyntheticMlt\n    {{just}} _assert-all-mlt-files-different\n\n# Run linting (format check)\nlint:\n    ./gradlew spotlessJavaCheck\n\n# Run tests\ntest:\n    ./gradlew test\n\n# Run Java cli tests\ntest-cli:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    JAVA=\"java -Dcom.google.protobuf.use_unsafe_pre22_gencode\"\n    ENCODE=\"$JAVA -jar ./mlt-cli/build/libs/encode.jar\"\n    DECODE=\"$JAVA -jar ./mlt-cli/build/libs/decode.jar\"\n    ./gradlew cli\n    $ENCODE --mvt ../test/fixtures/omt/10_530_682.mvt --mlt output/varint.mlt --decode\n    $ENCODE --mvt ../test/fixtures/omt/10_530_682.mvt --enable-fastpfor --enable-fsst --mlt output/advanced.mlt\n    $DECODE --mlt output/advanced.mlt\n    $ENCODE --mbtiles ../test/fixtures/omt.max1.mbtiles --outlines ALL --colmap-delim '[]name/[:_]/' --tessellate --sort-ids --coerce-mismatch --verbose warn --parallel\n    $ENCODE --pmtiles ../test/fixtures/omt-planet-20260112.mvt.max1.pmtiles --outlines ALL --colmap-delim '[]name/[:_]/' --tessellate --sort-ids --coerce-mismatch --verbose warn --parallel\n\n# Run tests with coverage\ntest-coverage:\n    ./gradlew jacocoTestReport\n\n# Test Maven local publishing\ntest-publish:\n    ./gradlew publishMavenPublicationToMavenLocal\n\n# Run benchmark tests\nbench:\n    ./gradlew test -Dbenchmark.iterations=200 -PincludeTags=benchmark\n    ./gradlew jmh\n\n_generate-one-expected-mlt file:\n    java \\\n        -Dcom.google.protobuf.use_unsafe_pre22_gencode \\\n        -jar mlt-cli/build/libs/encode.jar \\\n        --mvt {{quote(file)}} \\\n        --mlt {{quote(replace(without_extension(file) + '.mlt', '/fixtures/', '/expected/tag0x01/'))}} \\\n        --outlines ALL \\\n        --colmap-delim '[.*]name/[:_]/' \\\n        --enable-fsst \\\n        --tessellate \\\n        --coerce-mismatch \\\n        --verbose\n"
  },
  {
    "path": "java/resources/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\n\nproject(fsst)\n\nfind_package(Threads REQUIRED)\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n\nset(CMAKE_VERBOSE_MAKEFILE ON)\n\nif(APPLE)\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -arch x86_64 -arch arm64\")\nendif(APPLE)\n\nif (LINUX)\n  include(CheckCXXCompilerFlag)\n  check_cxx_compiler_flag(\"-march=native\" COMPILER_SUPPORTS_MARCH_NATIVE)\n  if(COMPILER_SUPPORTS_MARCH_NATIVE)\n      set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -march=native\")\n  endif()\nendif(LINUX)\n\nif(NOT CMAKE_BUILD_TYPE)\n set(CMAKE_BUILD_TYPE Release)\nendif()\n\nif(MSVC)\n    set(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} /O3 /DNDEBUG\")\nelse()\n    set(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} -O3 -DNDEBUG\")\nendif()\n\nfind_package(JNI)\nif (JNI_FOUND)\n    message (STATUS \"JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}\")\n    message (STATUS \"JNI_LIBRARIES=${JNI_LIBRARIES}\")\nendif()\n\ninclude(ExternalProject)\nExternalProject_Add(FsstDownload\n    GIT_REPOSITORY https://github.com/springmeyer/fsst\n    GIT_TAG bd766bc # https://github.com/springmeyer/fsst/tree/dane/cross-platform-build-fixes\n    INSTALL_COMMAND \"\"\n    PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libfsst-prefix\n    CMAKE_ARGS \"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}\"\n)\nSET(LIBFSST_LIB_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libfsst-prefix/src/FsstDownload-build)\nSET(LIBFSST_INC_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libfsst-prefix/src/FsstDownload)\n\nlink_directories(${LIBFSST_LIB_DIRECTORY})\n\nadd_library(libfsst STATIC IMPORTED)\n\n# TODO: find a way to avoid having to be explicit about library name differences between platforms\nif(MSVC)\n    set_target_properties(libfsst PROPERTIES IMPORTED_LOCATION ${LIBFSST_LIB_DIRECTORY}/Release/fsst.lib)\nelse()\n    set_target_properties(libfsst PROPERTIES IMPORTED_LOCATION ${LIBFSST_LIB_DIRECTORY}/libfsst.a)\nendif()\n\n\nadd_library(FsstWrapper MODULE ${CMAKE_CURRENT_SOURCE_DIR}/FsstWrapper.cpp)\nadd_dependencies(FsstWrapper FsstDownload)\nset_target_properties(FsstWrapper PROPERTIES\n    OUTPUT_NAME \"${output_name}\"\n    POSITION_INDEPENDENT_CODE ON\n    SUFFIX \".so\"\n    PREFIX \"\"\n)\ntarget_include_directories(FsstWrapper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${JNI_INCLUDE_DIRS} ${LIBFSST_INC_DIRECTORY})\ntarget_link_libraries(FsstWrapper libfsst)\n"
  },
  {
    "path": "java/resources/FsstWrapper.cpp",
    "content": "#include <cstdint>\n#include <iostream>\n#include <ostream>\n#include <vector>\n#include <FsstWrapper.h>\n#include <fsst.h>\n\nstruct SymbolTableStruct {\n    unsigned char symbolLengths[255];\n    unsigned long long symbols[255];\n    int nSymbols;\n    std::vector<unsigned char> compressedData;\n};\n\nSymbolTableStruct fsstCompress(std::vector<unsigned char> inputBytes) {\n    unsigned long n = 1;\n    auto** srcBuf = (uint8_t**)calloc(n, sizeof(uint8_t*));\n    auto** dstBuf = (uint8_t**)calloc(n, sizeof(uint8_t*));\n    auto* srcLen = (size_t*)calloc(n, sizeof(size_t));\n    auto* dstLen = (size_t*)calloc(n, sizeof(size_t));\n\n    srcBuf[0] = inputBytes.data();\n    srcLen[0] = inputBytes.size();\n    uint64_t before_size = inputBytes.size();\n\n    unsigned char serialized_encoder_buf[FSST_MAXHEADER];\n    fsst_encoder_t* encoder = fsst_create(n, srcLen, const_cast<const uint8_t**>(srcBuf), 0);\n    fsst_export(encoder, serialized_encoder_buf);\n\n    // the first 8 bytes of serialized_encoder_buf is where the version field is stored\n    uint64_t version;\n    memcpy(&version, serialized_encoder_buf, 8);\n\n    // nSymbols is stored in the second byte from the right of version\n    uint32_t nSymbols = (version >> 8) & 0xFF;\n\n    uint8_t lenHisto[8];\n    for (uint32_t i = 0; i < 8; i++) lenHisto[i] = serialized_encoder_buf[9 + i];\n\n    unsigned long output_buffer_size = 7 + 4 * before_size; // 1024 * 1024 * 1024\n    auto output_buffer = (uint8_t*)calloc(output_buffer_size, sizeof(uint8_t));\n\n    fsst_compress(\n        encoder, n, srcLen, const_cast<const uint8_t**>(srcBuf), output_buffer_size, output_buffer, dstLen, dstBuf);\n    size_t compressedDataLength = *dstLen;\n\n    fsst_decoder_t decoder;\n    fsst_import(&decoder, serialized_encoder_buf);\n\n    // Pack symbolTableStruct with relevant data\n    SymbolTableStruct symbolTableStruct{};\n    symbolTableStruct.nSymbols = nSymbols;\n    memcpy(symbolTableStruct.symbolLengths, decoder.len, sizeof(decoder.len));\n    memcpy(symbolTableStruct.symbols, decoder.symbol, sizeof(decoder.symbol));\n\n    for (size_t i = 0; i < n; ++i) {\n        for (size_t j = 0; j < compressedDataLength; ++j) {\n            symbolTableStruct.compressedData.push_back(dstBuf[i][j]);\n        }\n    }\n\n    fsst_destroy(encoder);\n\n    return symbolTableStruct;\n}\n\nJNIEXPORT jobject JNICALL Java_com_mlt_converter_encodings_fsst_FsstJni_compress(JNIEnv* env,\n                                                                                 jclass cls,\n                                                                                 jbyteArray inputBytes) {\n    jbyte* bytes = env->GetByteArrayElements(inputBytes, NULL);\n    jsize length = env->GetArrayLength(inputBytes);\n\n    std::vector<unsigned char> byteVector(bytes, bytes + length);\n\n    // Don't forget to release the memory as it's a direct pointer to the java array.\n    env->ReleaseByteArrayElements(inputBytes, bytes, 0);\n\n    SymbolTableStruct result = fsstCompress(byteVector);\n\n    // Convert symbolLengths array and symbols array\n    jsize nSymbols = (jint)result.nSymbols;\n    jintArray symbolLengthsArray = env->NewIntArray(nSymbols);\n    jint* tempIntData = new jint[nSymbols];\n\n    int totalSymbolLengths = 0;\n    for (int i = 0; i < nSymbols; i++) {\n        totalSymbolLengths += result.symbolLengths[i];\n    }\n\n    jbyteArray symbolsArray = env->NewByteArray(totalSymbolLengths);\n    int offset = 0;\n    for (int i = 0; i < nSymbols; i++) {\n        tempIntData[i] = (jint)result.symbolLengths[i];\n        env->SetByteArrayRegion(\n            symbolsArray, offset, tempIntData[i], reinterpret_cast<const jbyte*>(&result.symbols[i]));\n        offset += tempIntData[i];\n    }\n\n    env->SetIntArrayRegion(symbolLengthsArray, 0, nSymbols, tempIntData);\n    delete[] tempIntData;\n\n    // Convert compressedData to a Java byte array\n    auto compressedDataLength = result.compressedData.size();\n    jbyteArray compressedData = env->NewByteArray(compressedDataLength);\n    env->SetByteArrayRegion(compressedData, 0, compressedDataLength, (jbyte*)&result.compressedData[0]);\n\n    // Create the Java SymbolTable object\n    jclass symbolTableClass = env->FindClass(\"com/mlt/converter/encodings/fsst/SymbolTable\");\n    jmethodID symbolTableCtor = env->GetMethodID(symbolTableClass, \"<init>\", \"([B[I[BI)V\");\n    jobject javaSymbolTable = env->NewObject(\n        symbolTableClass, symbolTableCtor, symbolsArray, symbolLengthsArray, compressedData, length);\n\n    return javaSymbolTable;\n}\n"
  },
  {
    "path": "java/resources/FsstWrapper.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class FsstWrapper */\n\n#ifndef _Included_FsstWrapper\n#define _Included_FsstWrapper\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class:     FsstWrapper\n * Method:    compress\n * Signature: ([B)LSymbolTable;\n */\n\nJNIEXPORT jobject JNICALL Java_com_mlt_converter_encodings_fsst_FsstJni_compress(JNIEnv*, jclass, jbyteArray);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "java/resources/compile",
    "content": "#!/bin/bash\nset -euo pipefail\n\nif [[ -f build/FsstWrapper.so ]]; then\n    echo \"FsstWrapper.so exists, skipping compilation\"\n    echo \"  Remove ./build/FsstWrapper.so to recompile\"\n    echo \"  Remove ./build to reconfigure & compiled\"\nelse\n    echo \"FsstWrapper.so does not exist, building now\"\n    mkdir -p build\n    cd build\n    cmake ../resources\n    cmake --build . --config Release\nfi\necho \"Compilation successful\"\n"
  },
  {
    "path": "java/resources/compile-windows.bat",
    "content": "@echo off\n\nIF EXIST build\\Release\\FsstWrapper.so (\n    echo \"FsstWrapper.so exists, skipping compilation\"\n    echo \"  Remove ./build/Release/FsstWrapper.so to recompile\"\n    echo \"  Remove ./build to reconfigure & compiled\"\n) ELSE (\n    echo \"FsstWrapper.so does not exist, building now\"\n    mkdir build\n    cd build\n    cmake ../Resources\n    cmake --build . --config Release\n)\nIF %ERRORLEVEL% EQU 0 (\n    echo \"Compilation successful\"\n    exit /b 0\n) ELSE (\n    echo \"Compilation failed\"\n    exit /b 1\n)\n"
  },
  {
    "path": "java/settings.gradle",
    "content": "plugins {\n    id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'\n}\n\nrootProject.name = 'maplibre-tiles'\n\ninclude 'mlt-core'\ninclude 'mlt-cli'\ninclude 'mlt-tools'\n"
  },
  {
    "path": "java/tessellation/index.mjs",
    "content": "import bodyParser from \"body-parser\";\nimport earcut from \"earcut\";\nimport express from \"express\";\n\n/* Quick and dirty workaround to us the js version of earcut for pre-tessellating polygons in the java converter\n *  since the Java version of earcut seems to have minor issue */\nconst app = express();\napp.use(bodyParser.json({ limit: \"50mb\" }));\napp.use(bodyParser.urlencoded({ limit: \"50mb\", extended: true }));\napp.use(express.json());\n\napp.post(\"/tessellate\", (req, res) => {\n  const data = req.body;\n  const { vertices, holes } = data;\n\n  const indices = earcut(vertices, holes, 2);\n  const response = { indices };\n  res.json(response);\n});\n\nconst port = 3000;\napp.listen(port, () => {\n  console.log(`Server is running at http://localhost:${port}`);\n});\n"
  },
  {
    "path": "java/tessellation/package.json",
    "content": "{\n  \"name\": \"tessellation-server\",\n  \"version\": \"0.0.1-alpha.5\",\n  \"scripts\": {\n    \"serve\": \"node index.mjs\"\n  },\n  \"dependencies\": {\n    \"body-parser\": \"^1.20.3\",\n    \"earcut\": \"^3.0.1\",\n    \"express\": \"^4.21.1\"\n  }\n}\n"
  },
  {
    "path": "justfile",
    "content": "#!/usr/bin/env just --justfile\n\nmod cpp\nmod java\nmod rust\nmod ts\n\njust := quote(just_executable())\nci_mode := if env('CI', '') != '' {'1'} else {''}\n\n# By default, show the list of all available commands\n@_default:\n    {{just}} --list --list-submodules\n\nbench:\n    {{just}} rust::bench\n    {{just}} java::bench\n    {{just}} ts::bench\n\n# Run integration tests, and override what we expect the output to be with the actual output\nbless: _clean-int-test _test-run-int\n    {{just}} rust::bless\n    rm -rf test/expected && mv test/output test/expected\n\n# Delete all build files for multiple languages\nclean:\n    {{just}} rust::clean\n    {{just}} java::clean\n    {{just}} ts::clean\n    {{just}} cpp::clean\n\n# Run all formatting in every language\nfmt:\n    {{just}} rust::fmt\n    {{just}} java::fmt\n    {{just}} ts::fmt\n    {{just}} cpp::fmt\n\n# Run linting in every language. Run `just fmt` to fix formatting issues.\nlint:\n    {{just}} rust::lint\n    {{just}} java::lint\n    {{just}} ts::lint\n    {{just}} cpp::lint\n\n# Run all tests in every language, including integration tests\ntest: test-int\n    {{just}} rust::test\n    {{just}} java::test\n    {{just}} ts::test\n    {{just}} cpp::test\n\n# Run integration tests, ensuring that the output matches the expected output\ntest-int: _clean-int-test _test-run-int (_diff-dirs \"test/output\" \"test/expected\")\n\ndocs:\n\tdocker run --rm -it -p 8000:8000 -v ${PWD}:/docs zensical/zensical:latest\n\ndocs-build:\n    docker run --rm -v ${PWD}:/docs zensical/zensical:latest build\n\n# Extract version from a tag by removing language prefix and 'v' prefix\nci-extract-version language tag:\n    @echo \"{{replace(replace(tag, language + '-', ''), 'v', '')}}\"\n\n# Run the mlt CLI tool with the given arguments from current dir.\n[no-cd]\n[positional-arguments]  # avoids shell expansions\nmlt *args:\n    cargo run --manifest-path {{join(justfile_directory(), 'rust', 'Cargo.toml')}} --package mlt -- \"$@\"\n\n# Ensure a command is available\nassert-cmd command:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if ! type {{command}} > /dev/null; then\n        echo \"Command '{{command}}' could not be found. Please make sure it has been installed on your computer.\"\n        exit 1\n    fi\n\n# Install a Cargo tool if missing (uses cargo-binstall when available)\ncargo-install $COMMAND $INSTALL_CMD='' *args='':\n    #!/usr/bin/env bash\n    set -euo pipefail\n    binstall_args=\"{{ if env('CI', '') != '' {'--no-confirm --no-track --disable-telemetry'} else {''} }}\"\n    if ! command -v $COMMAND > /dev/null; then\n        if ! command -v cargo-binstall > /dev/null; then\n            echo \"$COMMAND could not be found. Installing it with    cargo install ${INSTALL_CMD:-$COMMAND} --locked {{args}}\"\n            cargo install ${INSTALL_CMD:-$COMMAND} --locked {{args}}\n        else\n            echo \"$COMMAND could not be found. Installing it with    cargo binstall ${INSTALL_CMD:-$COMMAND} $binstall_args --locked {{args}}\"\n            cargo binstall ${INSTALL_CMD:-$COMMAND} $binstall_args --locked {{args}}\n        fi\n    fi\n\n# Install the pmtiles CLI and Python pmtiles library (needed by download-benchmark-tiles)\ninstall-pmtiles:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if ! command -v pmtiles > /dev/null; then\n        OS=$(uname -s | tr '[:upper:]' '[:lower:]')\n        ARCH=$(uname -m)\n        case \"$OS-$ARCH\" in\n            linux-x86_64)   SUFFIX=\"Linux_x86_64\"   ;;\n            linux-aarch64)  SUFFIX=\"Linux_arm64\"     ;;\n            darwin-x86_64)  SUFFIX=\"Darwin_x86_64\"   ;;\n            darwin-arm64)   SUFFIX=\"Darwin_arm64\"    ;;\n            *) echo \"Unsupported platform: $OS-$ARCH\"; exit 1 ;;\n        esac\n        AUTH=()\n        if [ -n \"${GITHUB_TOKEN:-}\" ]; then\n            AUTH=(-H \"Authorization: Bearer ${GITHUB_TOKEN}\")\n        fi\n        ASSET_URL=$(curl -sSf \"${AUTH[@]}\" https://api.github.com/repos/protomaps/go-pmtiles/releases/latest | \\\n            jq -r \".assets[] | select(.name | test(\\\"${SUFFIX}\\\")) | .browser_download_url\")\n        if [ -z \"$ASSET_URL\" ] || [ \"$ASSET_URL\" = \"null\" ]; then\n            echo \"Could not find pmtiles release for $SUFFIX\"; exit 1\n        fi\n        curl -sSfL \"$ASSET_URL\" | sudo tar -xz -C /usr/local/bin pmtiles\n    fi\n    echo \"pmtiles: $(pmtiles version)\"\n    pip install --break-system-packages pmtiles 2>/dev/null \\\n        || pip install pmtiles\n\n# Make sure the git repo has no uncommitted changes. Fails only if CI envvar is set.\nassert-git-is-clean:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if [ -n \"$(git status --porcelain --untracked-files=all)\" ]; then\n        >&2 echo \"::error::git repo is not clean. Make sure compilation and tests artifacts are in the .gitignore, and no repo files are modified.\"\n        if [[ \"{{ci_mode}}\" == \"1\" ]]; then\n            >&2 echo \"::group::git status\"\n            git status\n            >&2 echo \"::endgroup::\"\n            >&2 echo \"::group::git diff (tracked changes)\"\n            git add . --intent-to-add\n            git --no-pager diff\n            >&2 echo \"::endgroup::\"\n            exit 1\n        else\n            >&2 echo \"git repo is not clean, but not failing because CI mode is not enabled.\"\n        fi\n    fi\n\n_clean-int-test:\n    rm -rf test/output && mkdir -p test/output\n\n_diff-dirs OUTPUT_DIR EXPECTED_DIR:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    echo \"** Comparing {{OUTPUT_DIR}} with {{EXPECTED_DIR}}...\"\n    if ! diff --brief --recursive --new-file {{OUTPUT_DIR}} {{EXPECTED_DIR}}; then\n        echo \"** Expected output does not match actual output\"\n        echo \"** You may want to run 'just bless' to update expected output\"\n        exit 1\n    else\n        echo \"** Expected output matches actual output\"\n    fi\n\n_test-run-int:\n    echo \"TODO: Add integration test command, outputting to test/output\"\n    echo \"fake output by copying expected into output so that the rest of the script works\"\n    # TODO: REMOVE THIS, and replace it with a real integration test run\n    cp -r test/expected/* test/output\n\n# Ensure there are no duplicate synthetic MLT files by comparing their hashes.\n_assert-all-mlt-files-different dir='test/synthetic':\n    #!/usr/bin/env bash\n    set -euo pipefail\n    all_hashes=$(find {{quote(dir)}} -name '*.mlt' -exec sha256sum {} \\; | sort)\n    duplicates=$(echo \"$all_hashes\" | awk '{print $1}' | uniq -d)\n    if [ -n \"$duplicates\" ]; then\n        echo \"::error::Duplicate synthetic MLT files found\"\n        while IFS= read -r hash; do\n            echo \"\"\n            echo \"$all_hashes\" | grep \"^$hash \" | awk '{print \"  - \" $2}'\n        done <<< \"$duplicates\"\n        exit 1\n    fi\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: MapLibre Tile Specification\nsite_url: https://www.maplibre.org/maplibre-tile-spec\nrepo_url: https://github.com/maplibre/maplibre-tile-spec\nsite_description: MapLibre Tile Specification\nedit_uri: edit/main/docs\nextra_css:\n  - assets/extra.css\ntheme:\n  name: 'material'\n  favicon: https://maplibre.org/favicon.ico\n  logo: assets/logo.svg\n  features:\n    - content.code.copy\n    - search.suggest\n    - navigation.instant\n    - navigation.sections\n    - content.action.edit\n    - toc.integrate\n  palette:\n    - media: \"(prefers-color-scheme)\"\n      toggle:\n        icon: material/brightness-auto\n        name: Switch to light mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to system preference\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\nmarkdown_extensions:\n  - pymdownx.highlight:\n      anchor_linenums: true\n      line_spans: __span\n      pygments_lang_class: true\n  - pymdownx.inlinehilite\n  - pymdownx.snippets:\n      dedent_subsections: true\n      check_paths: true\n      base_path: docs/snippets\n  - pymdownx.superfences:\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n  - pymdownx.escapeall:\n      hardbreak: True\n      nbsp: True\n  - admonition\n  - pymdownx.details\n  - footnotes\n  - attr_list\n  - md_in_html\n  - toc:\n      permalink: true\n      permalink_title: \"Link to this section\"\n      baselevel: 1\n      toc_depth: 6\nnav:\n  - Home: index.md\n  - Implementation Status: implementation-status.md\n  - Encoding Algorithms: encodings.md\n  - Specification: specification.md\n  - Geometry Spec: geometry.md\n\nextra:\n  social:\n    - icon: fontawesome/brands/mastodon\n      link: https://mastodon.social/@maplibre\n    - icon: fontawesome/brands/x-twitter\n      link: https://twitter.com/maplibre\n    - icon: fontawesome/brands/linkedin\n      link: https://www.linkedin.com/company/maplibre\n    - icon: fontawesome/brands/slack\n      link: https://osmus.slack.com/archives/C01G3D28DAB\n    - icon: fontawesome/brands/github\n      link: https://github.com/maplibre\nplugins:\n  - search\n  - callouts\n  - social:\n      cards_layout_options:\n        background_color: '#295DAA'\n  - panzoom\n  - add-number:\n      excludes: ['*']\n      includes:\n        - specification.md\nvalidation:\n  omitted_files: warn\n  absolute_links: warn\n  unrecognized_links: warn\n  anchors: warn\n"
  },
  {
    "path": "qgis/.gitignore",
    "content": "__pycache__/\n"
  },
  {
    "path": "qgis/README.md",
    "content": "# QGIS MLT Plugin\n\nA QGIS plugin to open **MapLibre Tile (MLT)** files, powered by the Rust\n`mlt` parser exposed to Python via [PyO3](https://pyo3.rs).\n\n## Architecture\n\n```\n┌─────────────────────┐\n│   QGIS Plugin       │  Python - registers menu action, creates memory layers\n│   (qgis/mlt_plugin) │\n└────────┬────────────┘\n         │ import\n┌────────▼────────────┐\n│   maplibre-tiles    │  Rust -> Python bridge (PyO3 + maturin)\n│   (rust/mlt-py)     │\n└────────┬────────────┘\n         │ depends on\n┌────────▼────────────┐\n│   mlt-core          │  Rust - zero-copy MLT binary parser\n│   (rust/mlt-core)   │\n└─────────────────────┘\n```\n\n## Prerequisites\n\n- **QGIS 3.22+** (tested with 3.34)\n- **Rust toolchain** (1.87+) — install via [rustup](https://rustup.rs)\n- **maturin** — PyO3 build tool (`pip install maturin`)\n- **Python 3.9+** (must match the Python that QGIS uses)\n\n## Installation\n\n### Step 1: Find QGIS's Python interpreter\n\nThe `mlt` native module must be built for the same Python that QGIS uses.\n\n```bash\n# Linux (system Python, usually the same one QGIS links to)\n/usr/bin/python3 -c \"import qgis.core; print('QGIS bindings OK')\"\n\n# macOS (QGIS bundles its own Python)\n/Applications/QGIS.app/Contents/MacOS/bin/python3 --version\n```\n\nUse whichever interpreter prints \"QGIS bindings OK\" in the commands below.\n\n### Step 2: Build and install the native module\n\n```bash\ncd rust/mlt-py\n\n# Option A: build a wheel, then install it\npip install maturin            # in any environment\nmaturin build --release --interpreter /usr/bin/python3\n\n# Install into user site-packages (visible to QGIS)\n/usr/bin/python3 -m pip install --user --break-system-packages \\\n    ../../rust/target/wheels/mlt-*.whl\n\n# Option B: if QGIS uses a virtualenv / conda, activate it first\n#   conda activate qgis-env   # or: source /path/to/venv/bin/activate\n#   maturin develop --release\n```\n\nVerify:\n\n```bash\n/usr/bin/python3 -c \"import maplibre_tiles; print(maplibre_tiles.list_layers(open('../../test/synthetic/0x01/polygon.mlt','rb').read()))\"\n# Expected: ['layer1']\n```\n\n### Step 3: Symlink the plugin into QGIS\n\n```bash\n# Linux\nln -sfn \"$(pwd)/../../qgis/mlt_plugin\" \\\n    ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/mlt_plugin\n\n# macOS\nln -sfn \"$(pwd)/../../qgis/mlt_plugin\" \\\n    ~/Library/Application\\ Support/QGIS/QGIS3/profiles/default/python/plugins/mlt_plugin\n\n# Windows (PowerShell, run as Admin)\n# New-Item -ItemType SymbolicLink `\n#   -Path \"$env:APPDATA\\QGIS\\QGIS3\\profiles\\default\\python\\plugins\\mlt_plugin\" `\n#   -Target (Resolve-Path \"..\\..\\qgis\\mlt_plugin\")\n```\n\n### Step 4: Enable in QGIS\n\n1. Launch QGIS\n2. **Plugins → Manage and Install Plugins**\n3. Search for **MLT Provider**, check the box to enable it\n4. A toolbar icon and menu entry appear under **Vector → MLT Provider → Open MLT File(s)…**\n\n## Usage\n\n### Opening a single tile\n\n1. Click **Open MLT File(s)…**\n2. Select one `.mlt` file\n3. A dialog appears with auto-detected **z/x/y** coordinates (parsed from the\n   filename, e.g. `14_8297_10749.mlt` → z=14, x=8297, y=10749)\n4. Confirm or edit the values, toggle **TMS y-axis** if needed\n5. Click **OK** — each MLT layer loads as a QGIS memory layer with real\n   EPSG:3857 coordinates\n\n### Opening multiple tiles at once\n\n1. Click **Open MLT File(s)…**\n2. Select multiple `.mlt` files (Ctrl+click / Shift+click)\n3. A table dialog shows all files with their auto-detected z/x/y\n4. Options:\n   - **TMS y-axis** — checked by default (correct for OpenMapTiles / MBTiles)\n   - **Merge same-named layers** — checked by default; combines features from\n     the same MLT layer across tiles into one QGIS layer for seamless viewing\n5. Click **OK** — e.g. selecting a 3×3 tile grid creates unified \"building\",\n   \"transportation\", \"water\" layers containing features from all 9 tiles\n\n### Coordinate conventions\n\n| Convention | y=0 is at | Used by |\n|---|---|---|\n| **TMS** (default) | South | OpenMapTiles, MBTiles, TileJSON |\n| **XYZ** | North | OSM raster tile servers |\n\nIf coordinates look wrong (features in the ocean), try toggling TMS.\n\nClick **Skip (raw coords)** to load tile-local integer coordinates without\ngeo-referencing (useful for inspecting raw tile data).\n\n## Troubleshooting\n\n**\"mlt module not found\"** — the native module isn't installed for QGIS's\nPython. Re-run Step 2 using the correct interpreter.\n\n**Features appear in the ocean** — toggle the TMS checkbox, or verify z/x/y\nvalues are correct.\n\n**Plugin not visible in QGIS** — check the symlink points to the right\ndirectory and that the `metadata.txt` file exists inside it.\n\n**Build fails with \"unsafe_code forbidden\"** — the `mlt-py` crate overrides\nthe workspace lint locally; make sure you're building from `rust/mlt-py/`,\nnot the workspace root.\n"
  },
  {
    "path": "qgis/mlt_plugin/__init__.py",
    "content": "\"\"\"QGIS plugin entry point for MLT (MapLibre Tile) file support.\"\"\"\n\n\ndef classFactory(iface):\n    from .plugin import MltPlugin\n\n    return MltPlugin(iface)\n"
  },
  {
    "path": "qgis/mlt_plugin/loader.py",
    "content": "\"\"\"Loads MLT files into QGIS memory layers using mlt.\"\"\"\n\nfrom collections import defaultdict\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, Tuple\n\nfrom qgis.core import (\n    Qgis,\n    QgsFeature,\n    QgsField,\n    QgsFields,\n    QgsGeometry,\n    QgsMessageLog,\n    QgsProject,\n    QgsVectorLayer,\n    QgsWkbTypes,\n)\nfrom qgis.PyQt.QtCore import QVariant\n\ntry:\n    import mlt\nexcept ImportError:\n    mlt = None\n\nPLUGIN_NAME = \"MLT Provider\"\n\nGEOM_TYPE_MAP = {\n    \"Point\": QgsWkbTypes.MultiPoint,\n    \"LineString\": QgsWkbTypes.MultiLineString,\n    \"Polygon\": QgsWkbTypes.MultiPolygon,\n    \"MultiPoint\": QgsWkbTypes.MultiPoint,\n    \"MultiLineString\": QgsWkbTypes.MultiLineString,\n    \"MultiPolygon\": QgsWkbTypes.MultiPolygon,\n}\n\n_PROMOTE_GEOM = {\n    \"Point\": \"MultiPoint\",\n    \"LineString\": \"MultiLineString\",\n    \"Polygon\": \"MultiPolygon\",\n}\n\n\ndef _canonical_geom_type(geom_type_str: str) -> str:\n    \"\"\"Map single-part types to their multi- equivalent for consistent grouping.\"\"\"\n    return _PROMOTE_GEOM.get(geom_type_str, geom_type_str)\n\nQVARIANT_MAP = {\n    bool: QVariant.Bool,\n    int: QVariant.LongLong,\n    float: QVariant.Double,\n    str: QVariant.String,\n}\n\n\ndef _ensure_mlt():\n    if mlt is None:\n        raise ImportError(\n            \"mlt module not found. \"\n            \"Install it with: pip install mlt  \"\n            \"(or build from rust/mlt-py with maturin)\"\n        )\n\n\ndef _infer_field_type(value) -> int:\n    return QVARIANT_MAP.get(type(value), QVariant.String)\n\n\ndef _qgs_geom_type_string(wkb_type) -> str:\n    mapping = {\n        QgsWkbTypes.Point: \"Point\",\n        QgsWkbTypes.LineString: \"LineString\",\n        QgsWkbTypes.Polygon: \"Polygon\",\n        QgsWkbTypes.MultiPoint: \"MultiPoint\",\n        QgsWkbTypes.MultiLineString: \"MultiLineString\",\n        QgsWkbTypes.MultiPolygon: \"MultiPolygon\",\n    }\n    return mapping.get(wkb_type, \"Point\")\n\n\ndef _group_features_by_geom_type(mlt_layer):\n    groups = {}\n    for feat in mlt_layer.features:\n        gt = _canonical_geom_type(feat.geometry_type)\n        groups.setdefault(gt, []).append(feat)\n    return groups\n\n\ndef _discover_fields(features) -> QgsFields:\n    field_types = {}\n    for feat in features:\n        props = feat.properties\n        for key, val in props.items():\n            if val is not None and key not in field_types:\n                field_types[key] = _infer_field_type(val)\n\n    fields = QgsFields()\n    for name, vtype in sorted(field_types.items()):\n        fields.append(QgsField(name, vtype))\n    return fields\n\n\ndef _make_qgs_features(features, vl, field_names) -> List[QgsFeature]:\n    qgs_features = []\n    for feat in features:\n        qf = QgsFeature(vl.fields())\n        geom = QgsGeometry()\n        geom.fromWkb(bytes(feat.wkb))\n        geom.convertToMultiType()\n        qf.setGeometry(geom)\n        for name in field_names:\n            val = feat.properties.get(name)\n            if val is not None:\n                qf.setAttribute(name, val)\n        qgs_features.append(qf)\n    return qgs_features\n\n\ndef _decode_file(file_path, zxy=None, tms=True):\n    data = Path(file_path).read_bytes()\n    if zxy is not None:\n        return mlt.decode_mlt(data, z=zxy[0], x=zxy[1], y=zxy[2], tms=tms)\n    return mlt.decode_mlt(data)\n\n\ndef _create_and_populate_layer(\n    layer_name: str,\n    geom_type_str: str,\n    features: list,\n    crs: str = \"EPSG:3857\",\n) -> Optional[QgsVectorLayer]:\n    \"\"\"Create a memory layer, populate it with features, and add to the project.\"\"\"\n    wkb_type = GEOM_TYPE_MAP.get(geom_type_str)\n    if wkb_type is None:\n        QgsMessageLog.logMessage(\n            f\"Skipping unknown geometry type: {geom_type_str}\",\n            PLUGIN_NAME,\n            Qgis.Warning,\n        )\n        return None\n\n    fields = _discover_fields(features)\n    geom_str = _qgs_geom_type_string(wkb_type)\n\n    uri = f\"{geom_str}?crs={crs}&index=yes\" if crs else f\"{geom_str}?index=yes\"\n    vl = QgsVectorLayer(uri, layer_name, \"memory\")\n    pr = vl.dataProvider()\n\n    pr.addAttributes(fields.toList())\n    vl.updateFields()\n\n    field_names = [fields.at(i).name() for i in range(fields.count())]\n    pr.addFeatures(_make_qgs_features(features, vl, field_names))\n    vl.updateExtents()\n\n    QgsProject.instance().addMapLayer(vl)\n    QgsMessageLog.logMessage(\n        f\"Loaded layer '{layer_name}' with {len(features)} features\",\n        PLUGIN_NAME,\n        Qgis.Info,\n    )\n    return vl\n\n\n# ── Public API ────────────────────────────────────────────────────────\n\n\ndef load_mlt_file(\n    file_path: str,\n    zxy: Optional[Tuple[int, int, int]] = None,\n    tms: bool = True,\n) -> List[QgsVectorLayer]:\n    \"\"\"Read a single MLT file and add its layers to the QGIS project.\"\"\"\n    _ensure_mlt()\n\n    mlt_layers = _decode_file(file_path, zxy=zxy, tms=tms)\n    stem = Path(file_path).stem\n    result = []\n\n    for mlt_layer in mlt_layers:\n        groups = _group_features_by_geom_type(mlt_layer)\n        for geom_type_str, features in groups.items():\n            name = f\"{stem} \\u2014 {mlt_layer.name}\"\n            if len(groups) > 1:\n                name += f\" ({geom_type_str})\"\n            crs = \"EPSG:3857\" if zxy is not None else \"\"\n            vl = _create_and_populate_layer(name, geom_type_str, features, crs=crs)\n            if vl:\n                result.append(vl)\n\n    return result\n\n\ndef load_mlt_files_merged(\n    file_paths: List[str],\n    file_coords: Optional[Dict[str, Tuple[int, int, int]]] = None,\n    tms: bool = True,\n) -> List[QgsVectorLayer]:\n    \"\"\"Read multiple MLT files and merge same-named layers into single QGIS layers.\n\n    Features from every tile that share the same MLT layer name and geometry\n    type are combined into one QGIS memory layer, giving a seamless multi-tile\n    view.\n\n    Args:\n        file_paths: List of .mlt file paths.\n        file_coords: Optional dict {path: (z, x, y)} for geo-referencing.\n                     Files not in the dict (or if None) use raw tile coords.\n        tms: TMS y-axis convention (default True).\n    \"\"\"\n    _ensure_mlt()\n\n    # Key: (layer_name, geom_type_str) -> list of mlt features\n    buckets: Dict[Tuple[str, str], list] = defaultdict(list)\n\n    for path in file_paths:\n        zxy = file_coords.get(path) if file_coords else None\n        mlt_layers = _decode_file(path, zxy=zxy, tms=tms)\n\n        for mlt_layer in mlt_layers:\n            for feat in mlt_layer.features:\n                bucket_key = (mlt_layer.name, _canonical_geom_type(feat.geometry_type))\n                buckets[bucket_key].append(feat)\n\n    result = []\n    n_files = len(file_paths)\n    label = f\"{n_files} tiles\"\n\n    for (layer_name, geom_type_str), features in sorted(buckets.items()):\n        display_name = f\"{label} \\u2014 {layer_name}\"\n        # Only disambiguate by geom type if the same layer name has multiple types\n        sibling_types = [\n            gt for (ln, gt) in buckets if ln == layer_name\n        ]\n        if len(sibling_types) > 1:\n            display_name += f\" ({geom_type_str})\"\n\n        crs = \"EPSG:3857\" if file_coords else \"\"\n        vl = _create_and_populate_layer(display_name, geom_type_str, features, crs=crs)\n        if vl:\n            result.append(vl)\n\n    return result\n"
  },
  {
    "path": "qgis/mlt_plugin/metadata.txt",
    "content": "[general]\nname=MLT Provider\nqgisMinimumVersion=3.22\ndescription=Open MapLibre Tile (MLT) files in QGIS using high-performance Rust decoding via mlt-py\nversion=0.1.0\nauthor=MapLibre Contributors\nemail=info@maplibre.org\nabout=Adds native support for the MapLibre Tile (MLT) columnar vector tile format. Uses Rust-based decoding (mlt) exposed through PyO3 for fast, reliable parsing. Features include drag-and-drop .mlt file loading, per-layer vector display, and full attribute support.\ntracker=https://github.com/maplibre/maplibre-tile-spec/issues\nrepository=https://github.com/maplibre/maplibre-tile-spec\ntags=vector tiles,mlt,maplibre,tiles\nhomepage=https://github.com/maplibre/maplibre-tile-spec\ncategory=Vector\nicon=\nexperimental=True\ndeprecated=False\nchangelog=\n    0.1.0 - Initial release with MLT file reading support\n"
  },
  {
    "path": "qgis/mlt_plugin/plugin.py",
    "content": "\"\"\"QGIS Plugin class that registers the MLT file handler.\"\"\"\n\nfrom pathlib import Path\n\nfrom qgis.PyQt.QtGui import QIcon\nfrom qgis.PyQt.QtWidgets import QAction, QFileDialog\nfrom qgis.core import Qgis, QgsMessageLog\n\nfrom .loader import load_mlt_file, load_mlt_files_merged\nfrom .tile_coords import (\n    MultipleTileCoordDialog,\n    TileCoordDialog,\n    parse_zxy_from_path,\n)\n\nPLUGIN_NAME = \"MLT Provider\"\n\n\nclass MltPlugin:\n    def __init__(self, iface):\n        self.iface = iface\n        self.action = None\n\n    def initGui(self):\n        icon = QIcon()\n        self.action = QAction(icon, \"Open MLT File(s)\\u2026\", self.iface.mainWindow())\n        self.action.triggered.connect(self.open_file_dialog)\n        self.iface.addToolBarIcon(self.action)\n        self.iface.addPluginToVectorMenu(PLUGIN_NAME, self.action)\n\n    def unload(self):\n        if self.action:\n            self.iface.removePluginVectorMenu(PLUGIN_NAME, self.action)\n            self.iface.removeToolBarIcon(self.action)\n\n    def open_file_dialog(self):\n        paths, _ = QFileDialog.getOpenFileNames(\n            self.iface.mainWindow(),\n            \"Open MapLibre Tile(s)\",\n            \"\",\n            \"MLT Files (*.mlt);;All Files (*)\",\n        )\n        if not paths:\n            return\n\n        if len(paths) == 1:\n            self._load_single(paths[0])\n        else:\n            self._load_multiple(paths)\n\n    def _load_single(self, path: str):\n        guess = parse_zxy_from_path(path)\n        dlg = TileCoordDialog(self.iface.mainWindow(), initial=guess)\n        zxy = None\n        tms = True\n        if dlg.exec_():\n            zxy = dlg.zxy()\n            tms = dlg.tms\n        elif not dlg.skipped:\n            return\n\n        try:\n            layers = load_mlt_file(path, zxy=zxy, tms=tms)\n            self._report_result(layers, [path], zxy is not None)\n        except Exception as exc:\n            self._report_error(exc)\n\n    def _load_multiple(self, paths: list):\n        files_with_coords = [\n            (p, parse_zxy_from_path(p)) for p in paths\n        ]\n        dlg = MultipleTileCoordDialog(\n            self.iface.mainWindow(), files_with_coords=files_with_coords\n        )\n\n        use_coords = False\n        tms = True\n        merge = True\n        file_coords = None\n\n        if dlg.exec_():\n            use_coords = True\n            tms = dlg.tms\n            merge = dlg.merge\n            file_coords = dlg.file_coords()\n        elif not dlg.skipped:\n            return\n        else:\n            merge = True\n\n        try:\n            if merge:\n                layers = load_mlt_files_merged(\n                    paths,\n                    file_coords=file_coords if use_coords else None,\n                    tms=tms,\n                )\n            else:\n                layers = []\n                for p in paths:\n                    zxy = file_coords.get(p) if file_coords else None\n                    layers.extend(load_mlt_file(p, zxy=zxy, tms=tms))\n\n            self._report_result(layers, paths, use_coords)\n        except Exception as exc:\n            self._report_error(exc)\n\n    def _report_result(self, layers, paths, georeferenced):\n        if not layers:\n            self.iface.messageBar().pushMessage(\n                PLUGIN_NAME,\n                \"No layers found in the MLT file(s).\",\n                level=Qgis.Warning,\n                duration=5,\n            )\n        else:\n            n_files = len(paths)\n            file_str = \"file\" if n_files == 1 else f\"{n_files} files\"\n            coord_msg = \" (georeferenced)\" if georeferenced else \" (raw tile coords)\"\n            self.iface.messageBar().pushMessage(\n                PLUGIN_NAME,\n                f\"Loaded {len(layers)} layer(s) from {file_str}{coord_msg}\",\n                level=Qgis.Info,\n                duration=5,\n            )\n\n    def _report_error(self, exc):\n        QgsMessageLog.logMessage(str(exc), PLUGIN_NAME, Qgis.Critical)\n        self.iface.messageBar().pushMessage(\n            PLUGIN_NAME,\n            f\"Failed to load MLT file: {exc}\",\n            level=Qgis.Critical,\n            duration=10,\n        )\n"
  },
  {
    "path": "qgis/mlt_plugin/tile_coords.py",
    "content": "\"\"\"Parse and prompt for tile z/x/y coordinates.\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import Dict, List, Optional, Tuple\n\nfrom qgis.PyQt.QtCore import Qt\nfrom qgis.PyQt.QtWidgets import (\n    QCheckBox,\n    QDialog,\n    QDialogButtonBox,\n    QFormLayout,\n    QHeaderView,\n    QLabel,\n    QSpinBox,\n    QTableWidget,\n    QTableWidgetItem,\n    QVBoxLayout,\n)\n\nZXY = Tuple[int, int, int]\n\n_FNAME_RE = re.compile(r\"(\\d{1,2})[_\\-](\\d+)[_\\-](\\d+)\")\n\n\ndef parse_zxy_from_path(file_path: str) -> Optional[ZXY]:\n    \"\"\"Try to extract z/x/y from the file name or parent directories.\n\n    Recognises:\n      - 14_8297_10749.mlt        (underscore-separated)\n      - 14-8297-10749.mlt        (dash-separated)\n      - .../14/8297/10749.mlt    (directory hierarchy)\n    \"\"\"\n    p = Path(file_path)\n\n    m = _FNAME_RE.search(p.stem)\n    if m:\n        return int(m.group(1)), int(m.group(2)), int(m.group(3))\n\n    try:\n        y_val = int(p.stem)\n        x_val = int(p.parent.name)\n        z_val = int(p.parent.parent.name)\n        if 0 <= z_val <= 30:\n            return z_val, x_val, y_val\n    except (ValueError, IndexError):\n        pass\n\n    return None\n\n\nclass TileCoordDialog(QDialog):\n    \"\"\"Dialog for a single tile — enter or confirm z/x/y coordinates.\"\"\"\n\n    def __init__(self, parent=None, initial: Optional[ZXY] = None):\n        super().__init__(parent)\n        self.setWindowTitle(\"MLT Tile Coordinates\")\n        self.setMinimumWidth(320)\n\n        layout = QVBoxLayout(self)\n\n        info = QLabel(\n            \"Vector tiles use local coordinates. To place features\\n\"\n            \"on the map correctly, enter the tile's z/x/y address.\"\n        )\n        info.setWordWrap(True)\n        layout.addWidget(info)\n\n        form = QFormLayout()\n\n        self.z_spin = QSpinBox()\n        self.z_spin.setRange(0, 30)\n        self.z_spin.setValue(initial[0] if initial else 14)\n        form.addRow(\"Zoom (z):\", self.z_spin)\n\n        self.x_spin = QSpinBox()\n        self.x_spin.setRange(0, 2**30 - 1)\n        self.x_spin.setValue(initial[1] if initial else 0)\n        form.addRow(\"Column (x):\", self.x_spin)\n\n        self.y_spin = QSpinBox()\n        self.y_spin.setRange(0, 2**30 - 1)\n        self.y_spin.setValue(initial[2] if initial else 0)\n        form.addRow(\"Row (y):\", self.y_spin)\n\n        self.tms_check = QCheckBox(\"TMS y-axis (y=0 at south)\")\n        self.tms_check.setChecked(True)\n        self.tms_check.setToolTip(\n            \"Check for OpenMapTiles, MBTiles, TileJSON sources.\\n\"\n            \"Uncheck for OSM slippy-map / XYZ tiles (y=0 at north).\"\n        )\n        form.addRow(\"Convention:\", self.tms_check)\n\n        layout.addLayout(form)\n\n        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)\n        skip = buttons.addButton(\"Skip (raw coords)\", QDialogButtonBox.RejectRole)\n        skip.clicked.connect(self._on_skip)\n        buttons.accepted.connect(self.accept)\n        buttons.rejected.connect(self.reject)\n        layout.addWidget(buttons)\n\n        self._skipped = False\n\n    def _on_skip(self):\n        self._skipped = True\n        self.reject()\n\n    @property\n    def skipped(self) -> bool:\n        return self._skipped\n\n    def zxy(self) -> ZXY:\n        return self.z_spin.value(), self.x_spin.value(), self.y_spin.value()\n\n    @property\n    def tms(self) -> bool:\n        return self.tms_check.isChecked()\n\n\nclass MultipleTileCoordDialog(QDialog):\n    \"\"\"Dialog for multiple tiles — shows an editable table of detected z/x/y per file.\"\"\"\n\n    def __init__(\n        self,\n        parent=None,\n        files_with_coords: Optional[List[Tuple[str, Optional[ZXY]]]] = None,\n    ):\n        super().__init__(parent)\n        self.setWindowTitle(\"MLT Tile Coordinates\")\n        self.setMinimumWidth(600)\n        self.setMinimumHeight(400)\n\n        self._files = files_with_coords or []\n\n        layout = QVBoxLayout(self)\n\n        detected = sum(1 for _, c in self._files if c is not None)\n        total = len(self._files)\n        info = QLabel(\n            f\"{total} tiles selected. Coordinates auto-detected for \"\n            f\"{detected}/{total} files.\\n\"\n            \"Edit the table below to correct any values.\"\n        )\n        info.setWordWrap(True)\n        layout.addWidget(info)\n\n        self.table = QTableWidget(total, 4)\n        self.table.setHorizontalHeaderLabels([\"File\", \"z\", \"x\", \"y\"])\n        header = self.table.horizontalHeader()\n        header.setSectionResizeMode(0, QHeaderView.Stretch)\n        for col in (1, 2, 3):\n            header.setSectionResizeMode(col, QHeaderView.ResizeToContents)\n\n        for row, (path, coords) in enumerate(self._files):\n            name_item = QTableWidgetItem(Path(path).name)\n            name_item.setFlags(name_item.flags() & ~Qt.ItemIsEditable)\n            self.table.setItem(row, 0, name_item)\n\n            z_val, x_val, y_val = coords if coords else (0, 0, 0)\n\n            z_spin = QSpinBox()\n            z_spin.setRange(0, 30)\n            z_spin.setValue(z_val)\n            self.table.setCellWidget(row, 1, z_spin)\n\n            x_spin = QSpinBox()\n            x_spin.setRange(0, 2**30 - 1)\n            x_spin.setValue(x_val)\n            self.table.setCellWidget(row, 2, x_spin)\n\n            y_spin = QSpinBox()\n            y_spin.setRange(0, 2**30 - 1)\n            y_spin.setValue(y_val)\n            self.table.setCellWidget(row, 3, y_spin)\n\n        layout.addWidget(self.table)\n\n        self.tms_check = QCheckBox(\"TMS y-axis (y=0 at south)\")\n        self.tms_check.setChecked(True)\n        self.tms_check.setToolTip(\n            \"Check for OpenMapTiles, MBTiles, TileJSON sources.\\n\"\n            \"Uncheck for OSM slippy-map / XYZ tiles (y=0 at north).\"\n        )\n        layout.addWidget(self.tms_check)\n\n        self.merge_check = QCheckBox(\"Merge same-named layers across tiles\")\n        self.merge_check.setChecked(True)\n        self.merge_check.setToolTip(\n            \"When checked, layers with the same name from different tiles\\n\"\n            \"are combined into a single QGIS layer for seamless viewing.\"\n        )\n        layout.addWidget(self.merge_check)\n\n        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)\n        skip = buttons.addButton(\"Skip (raw coords)\", QDialogButtonBox.RejectRole)\n        skip.clicked.connect(self._on_skip)\n        buttons.accepted.connect(self.accept)\n        buttons.rejected.connect(self.reject)\n        layout.addWidget(buttons)\n\n        self._skipped = False\n\n    def _on_skip(self):\n        self._skipped = True\n        self.reject()\n\n    @property\n    def skipped(self) -> bool:\n        return self._skipped\n\n    @property\n    def tms(self) -> bool:\n        return self.tms_check.isChecked()\n\n    @property\n    def merge(self) -> bool:\n        return self.merge_check.isChecked()\n\n    def file_coords(self) -> Dict[str, Optional[ZXY]]:\n        \"\"\"Return {file_path: (z, x, y)} for each file.\"\"\"\n        result = {}\n        for row, (path, _) in enumerate(self._files):\n            z = self.table.cellWidget(row, 1).value()\n            x = self.table.cellWidget(row, 2).value()\n            y = self.table.cellWidget(row, 3).value()\n            result[path] = (z, x, y)\n        return result\n"
  },
  {
    "path": "release-plz.toml",
    "content": "[workspace]\nallow_dirty = false\nchangelog_update = true\ndependencies_update = true\ngit_release_enable = true\ngit_release_name = \"rust-{{ package }}-v{{ version }}\"\ngit_release_body = \"\"\"\n{{ changelog }}\n{% if remote.contributors %}\n\n### Contributors\n{% for contributor in remote.contributors %}\n* @{{ contributor.username }}\n{% endfor %}\n{% endif %}\n\"\"\"\ngit_tag_enable = true\ngit_tag_name = \"rust-{{ package }}-v{{ version }}\"\npr_branch_prefix = \"release-plz-\"\npr_labels = [\"release\"]\npr_name = \"chore(rust): release{% if package %} {{ package }}{% endif %}{% if version %} v{{ version }}{% endif %}\"\npublish_allow_dirty = false\npublish_no_verify = true # we have already thoroughly verified that all aspects of our project work in CI before this step\nsemver_check = true\npublish_timeout = \"10m\" # set a timeout for `cargo publish`\npublish = true\n\n[[package]]\nname = \"mlt\"\n\n[[package]]\nname = \"mlt-core\"\n\n[[package]]\nname = \"mlt-py\"\ngit_tag_name = \"python-mlt-v{{ version }}\"\ngit_release_name = \"python-mlt-v{{ version }}\"\n\n[[package]]\nname = \"mlt-synthetics\"\npublish = false\n\n[changelog]\n# always include commits with breaking changes in the changelog\nprotect_breaking_commits = true\n"
  },
  {
    "path": "rust/.gitignore",
    "content": "**/*.rs.bk\n*.pdb\n*.proptest-regressions\n.idea/\n.vscode/\ndebug/\nproptest-regressions/\ntarget/\ntemp/\ntmp/\nvenv/\nrustc-ice-*.txt\n"
  },
  {
    "path": "rust/CONTRIBUTING.md",
    "content": "# Contributing to Rust MLT\n\n## Development Workflow\nAll commands must be run from the repository root using [`just`](https://github.com/casey/just).\nDo **not** use bare `cargo` commands; `just` recipes ensure the correct feature flags and CI-matching environments.\n\nBefore committing, make sure that `just rust::fmt`, `just rust::lint`, and `just rust::test` pass.\n\n| Command                              | Description                                                             |\n|:-------------------------------------|:------------------------------------------------------------------------|\n| `just rust::check`                   | Fast compilation check (no binaries).                                   |\n| `just rust::test`                    | Run full test suite (including property-based tests).                   |\n| `just rust::fmt`                     | Format code according to project style.                                 |\n| `just rust::lint`                    | Run `clippy` lints.                                                     |\n| `just rust::generate-synthetic-mlts` | Generate synthetic MLT files for testing. Do not delete files manually. |\n| `just rust::bless`                   | Regenerate expected `insta` test snapshots.                             |\n\n## Code Structure\n* Most structs and enums live in `model.rs` files, while the specific functionality for those types live in impl blocks of various files like `decoding.rs`, `encoding.rs`, etc. This is not very Rust-idiomatic, but it allows for better organization of the codebase by feature (e.g., all decoding logic in one file, all encoding logic in another file) rather than by type.\n* We try to keep encoding and decoding logic separate, as they have different requirements and optimizations. For example, decoding can be more flexible and allow for partial lazy decoding, while encoding needs to operate on owned data structures and may require more complex transformations and optimizations (e.g., reordering features for better compression).\n\n## Decoding Data\nWhen decoding data, `mlt-core` moves through a strict linear pipeline, minimizing unnecessary allocations and copies.  Both raw and parsed data is stored in the container structs (e.g., `TileLayer`) as variants of the `LazyParsed<Raw, Parsed>` generic enum to allow for partial lazy decoding for any column like `id`, `geometry`, `property`, `sub-property`. Some internal owned types may be used inside both the `Parsed*` (decoding) and `Staged*` (encoding) data structures. `Parsed*` structs should not be created in any way other than from the `Raw*` structs. When testing, create corresponding `Staged*` struct to compare with the Parsed* ones - there should be an equality trait for them all. Some types might be shared between encoding and decoding types. If shared, these types should not have any intermediate prefixes, i.e. raw, parsed, staged, or encoded. If conflicted, the type should be named like `Owned*` or just by its name without a prefix.\n\n\n| Stage | Prefix         | Ownership                | Purpose                                                                       |\n|:------|:---------------|:-------------------------|:------------------------------------------------------------------------------|\n| **-** | `<Name>`       | Borrowed (`&'a`)         | Container structs to allow partial lazy decoding.                             |\n| **1** | `Raw<Name>`    | Borrowed (`&'a`)         | Zero-copy views of input bytes. No allocation.                                |\n| **2** | `Parsed<Name>` | Owned or Borrowed (`'a`) | Fully decoded Rust values (e.g., `Vec<u64>`), or could reference input bytes. |\n| **3** | `Tile*`        | Owned                    | **Row-oriented** features using `mlt_core::geo_types::Geometry<i32>` for geometries.    |\n\n### Notes\n* **Forward Only:** Data moves `1` -> `3`. No backwards conversions (e.g., `Tile*` cannot become `Parsed*`).\n* **Slicing:** Original input bytes are sliced into `Raw*` structures. No copying. Uses `Raw*::parse(&[u8])` -> `MltRefResult<Raw*>` constructors. Not to be confused with `Parsed*` parsing into Parsed stage.\n* **Parsing:** `Raw*` `->` `Parsed*` via `TryFrom`.\n* **Row-based tiles:** `Parsed*` `->` `Tile*` via `TryFrom`. This is mostly used for GeoJSON generation by CLI and debugging tools, and should probably not be needed for most users like data access via WASM.\n* **No 'static** All types in the decoding pipeline should have real lifetimes tied to original byte buffer, not `'static`. For testing, simply create corresponding `Staged*` instance to compare.\n\n\n## Encoding Data\nEncoding is more complex, and requires owned data structures to support optimizations and transformations. The pipeline is as follows:\n\n| Stage | Prefix     | Ownership             | Purpose                                                                    |\n|:------|:-----------|:----------------------|:---------------------------------------------------------------------------|\n| **1** | `Tile*`    | Owned                 | **Row-oriented** features using `geo_types::Geometry<i32>` for geometries. |\n| **2** | `Staged*`  | Owned                 | **Columnar** data being prepared for encoding.                             |\n| **3** | `Encoded*` | Owned                 | Wire-ready byte buffers.                                                   |\n\n### Notes\n* **Forward Only:** Data moves `1` -> `3`. No backwards conversions (e.g., `Encoded` cannot become `Staged`).\n* All progression steps will use an `Encoder*` types that has some state of **how** to encode, the data of the stage, and return the next stage types, e.g. `TileLayer` via `Tile01Encoder::encode(&mut self, data: &mut TileLayer)` -> `MltResult<StagedLayer>` `->` `StagedLayer`. Note that both `self` and `data` are mutable references, as the encoder may need to mutate internal state and the data being encoded (e.g., for optimizations like reordering features or updating profiling information).\n* **Staging:** `Tile*` `->` `Staged*`.\n* **Encoding:** `Staged*` `->` `Encoded*`.\n* **Serialization:** `Encoded*` `->` bytes via `write_to(&mut writer)`.\n* In some use cases, user may want to construct `Staged*` directly (e.g., to generate synthetic MLT files or in benchmarking), and skip the `Tile*` stage.\n* There should not be a need to convert from `Parsed*` to `Staged*`, as the former is for decoding and the latter is for encoding. For round trip testing, the workflow should be `Tile*` `->` `Staged*` `->` `Encoded*` `->` `Vec<u8>` buffer `->` `Raw*` `->` `Parsed*` `->` `Tile*`.  It should be possible to compare `Staged*` and `Parsed*` for testing purposes, but not convert from one to another.\n* **No decode when encoding:** The encoder moves only from higher-level to lower-level (e.g. it may try multiple sortings or encodings at `TileLayer` level and keep the best result). It must never decode encoded data back into a decoded representation. Round-trip verification belongs in tests only, via the full bytes pipeline (encode → bytes → parse → decode).\n"
  },
  {
    "path": "rust/Cargo.toml",
    "content": "[workspace]\nresolver = \"3\"\nmembers = [\"mlt\", \"mlt-core\", \"mlt-py\", \"mlt-synthetics\", \"mlt-wasm\"]\ndefault-members = [\"mlt\", \"mlt-core\", \"mlt-synthetics\"]\n# Excluding mlt-core/fuzz because cargo-fuzz does not have workspace support\nexclude = [\"mlt-core/fuzz\"]\n\n[workspace.package]\nauthors = [\"Maplibre Contributors\"]\ncategories = [\"science::geo\"]\nedition = \"2024\"\nhomepage = \"https://maplibre.org\"\nkeywords = [\"maplibre\", \"tile\", \"mlt\", \"mvt\", \"map\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/maplibre/maplibre-tile-spec\"\nrust-version = \"1.92\"\n\n[workspace.dependencies]\nanyhow = \"1\"\narbitrary = { version = \"1.4\", features = [\"derive\"] }\nbitvec = \"1\"\nbrotli = \"8\"\nbytemuck = \"1.25.0\"\nbytes = \"1\"\nclap = { version = \"4.6.0\", features = [\"derive\"] }\ncriterion = { version = \"0.8\", features = [\"html_reports\"] }\ncrossterm = \"0.29.0\"\nderive-debug = \"0.1.2\"\nenum_dispatch = \"0.3\"\nfastpfor = { version = \"0.9\", features = [\"rust\"] }\nflate2 = \"1\"\nfsst-rs = \"0.5\"\nfutures = \"0.3\"\ngeo = { version = \"0.33.1\", default-features = false }\ngeo-types = \"0.7.19\"\nglob = \"0.3\"\nglobset = \"0.4\"\nhex = \"0.4.3\"\nhilbert_2d = \"1.1.0\"\nhotpath = \"0.15\"\nindicatif = \"0.18\"\ninsta = \"1.47.2\"\ninteger-encoding = \"4.0.2\"\njs-sys = \"0.3\"\nmartin-tile-utils = \"0.7\"\nmbtiles = { version = \"0.17\", default-features = false, features = [\"transcode\"] }\nmlt-core = { version = \"0.9.0\", path = \"mlt-core\" }\nmoka = { version = \"0.12\", features = [\"sync\"] }\nmvt-reader = \"2.3.0\"\nnum-traits = \"0.2.19\"\nnum_enum = \"0.7.6\"\npretty_assertions = \"1.4\"\nprobabilistic-collections = \"0.7\"\nproptest = \"1.11\"\nproptest-derive = \"0.8\"\npyo3 = \"0.28.3\"\npyo3-stub-gen = \"0.22.2\"\nratatui = \"0.30.0\"\nrayon = \"1\"\nrstar = \"0.12\"\nrstest = \"0.26.1\"\nserde = { version = \"1\", features = [\"derive\"] }\nserde_json = \"1.0.149\"\nsize_format = \"1\"\nsqlx = { version = \"0.8\", default-features = false, features = [\"sqlite\", \"runtime-tokio\"] }\nstrum = { version = \"0.28.0\", default-features = false, features = [\"derive\"] }\ntabled = \"0.20\"\ntest_each_file = \"0.3.5\"\nthiserror = \"2.0.11\"\nthousands = \"0.2\"\ntokio = { version = \"1\", features = [\"macros\", \"rt\", \"sync\", \"time\"] }\nunion-find = \"0.4.4\"\nwalkdir = \"2\"\nwasm-bindgen = \"0.2\"\nwide = \"1.1.1\"\nxxhash-rust = { version = \"0.8\", features = [\"xxh3\"] }\nzigzag = \"0.1.0\"\nzstd = \"0.13\"\n\n[workspace.lints.rust]\nunsafe_code = \"forbid\"\nunused_qualifications = \"warn\"\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(fuzzing)'] }\n\n[workspace.lints.clippy]\ncargo = { level = \"warn\", priority = -1 }\npedantic = { level = \"warn\", priority = -1 }\n\n# Restrictions\npanic_in_result_fn = \"warn\"\ntodo = \"warn\"\nunimplemented = \"warn\"\nuse_self = \"warn\"\n\n# Too noisy\nmissing_errors_doc = \"allow\"\nmissing_panics_doc = \"allow\"\nmultiple_crate_versions = \"allow\"\ntoo_many_lines = \"allow\"\n"
  },
  {
    "path": "rust/README.md",
    "content": "# Rust based MLT tooling and libraries\n\nIn this directory you will find Rust based libraries and tools for MLT.\n- [`mlt-core`](./mlt-core/README.md) is the rust library for MLT.\n- [`mlt`](./mlt/README.md) is our CLI tool for exploring and working with MLTs.\n"
  },
  {
    "path": "rust/bench_param.sh",
    "content": "#!/usr/bin/env bash\n# Usage: ./bench_param.sh <file> <sed-pattern> <value1> [value2] ...\n#\n# <sed-pattern> uses \\1 as the placeholder for the value.\n# The script substitutes each value, rebuilds, runs N iterations,\n# and reports avg size ± spread and avg time ± spread.\n#\n# Examples:\n#   ./bench_param.sh mlt-core/src/encoder/geometry/encode.rs \\\n#       's/MORTON_UNIQUENESS_THRESHOLD: f64 = .*/MORTON_UNIQUENESS_THRESHOLD: f64 = \\1;/' \\\n#       0.3 0.4 0.5 0.6\n#\n#   ./bench_param.sh mlt-core/src/encoder/property/strings.rs \\\n#       's/FSST_OVERHEAD_THRESHOLD: usize = .*/FSST_OVERHEAD_THRESHOLD: usize = \\1;/' \\\n#       2_048 4_096 8_192\n\nset -euo pipefail\n\nRUNS=1\nINPUT=\"../data/germany.mbtiles\"\nFILE=\"$1\"; shift\nSED_TEMPLATE=\"$1\"; shift\nVALUES=(\"$@\")\n\nif [[ ${#VALUES[@]} -eq 0 ]]; then\n  echo \"Usage: $0 <file> <sed-pattern> <value1> [value2] ...\"\n  exit 1\nfi\n\nprintf \"%-20s %12s %12s %12s %12s\\n\" \"Value\" \"Avg (bytes)\" \"Spread\" \"Avg (ms)\" \"Spread (ms)\"\nprintf '%.0s─' {1..72}; echo\n\nfor val in \"${VALUES[@]}\"; do\n  sed_cmd=\"${SED_TEMPLATE//\\\\1/$val}\"\n  sed -i \"$sed_cmd\" \"$FILE\"\n\n  cargo build --release -p mlt 2>/dev/null\n\n  sizes=()\n  times=()\n  for ((i=1; i<=RUNS; i++)); do\n    out=\"/tmp/bench_param_$$.mbtiles\"\n    rm -f \"$out\"\n    start_ns=$(date +%s%N)\n    ./target/release/mlt convert \"$INPUT\" \"$out\" 1>/dev/null\n    end_ns=$(date +%s%N)\n    elapsed_ms=$(( (end_ns - start_ns) / 1000000 ))\n    times+=(\"$elapsed_ms\")\n    s=$(stat --printf='%s' \"$out\")\n    sizes+=(\"$s\")\n    rm -f \"$out\"\n  done\n\n  size_csv=$(IFS=,; echo \"${sizes[*]}\")\n  time_csv=$(IFS=,; echo \"${times[*]}\")\n  read -r savg smn smx tavg tmn tmx < <(python3 -c \"\ns = [$size_csv]\nt = [$time_csv]\nprint(sum(s)//len(s), min(s), max(s), sum(t)//len(t), min(t), max(t))\n\")\n  sspread=$((smx - smn))\n  tspread=$((tmx - tmn))\n  printf \"%-20s %12s %12s %12s %12s\\n\" \"$val\" \"$savg\" \"±$sspread\" \"${tavg}ms\" \"±${tspread}ms\"\ndone\n"
  },
  {
    "path": "rust/clippy.toml",
    "content": "allow-unwrap-in-tests = true\navoid-breaking-exported-api = false\n\ndisallowed-methods = [\n    \"alloc::boxed::Box::leak\",\n    \"alloc::vec::Vec::leak\",\n]\n"
  },
  {
    "path": "rust/mlt/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.1.11](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.10...rust-mlt-v0.1.11) - 2026-04-29\n\n### Added\n\n- *(rust)* add secondary Hilbert dictionary sorting on select tiles ([#1349](https://github.com/maplibre/maplibre-tile-spec/pull/1349))\n\n### Other\n\n- *(rust)* IdValues→ParsedId+StagedId, simplify presence ([#1334](https://github.com/maplibre/maplibre-tile-spec/pull/1334))\n- *(rust)* Improve the performance of `mlt convert` by caching and special-casing ([#1286](https://github.com/maplibre/maplibre-tile-spec/pull/1286))\n\n## [0.1.10](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.9...rust-mlt-v0.1.10) - 2026-04-18\n\n### Added\n\n- *(rust)* mlt UI to browse .mbtiles ([#1306](https://github.com/maplibre/maplibre-tile-spec/pull/1306))\n\n### Other\n\n- remove ALP everywhere - it was never implemented ([#1128](https://github.com/maplibre/maplibre-tile-spec/pull/1128))\n- *(rust)* rm RawStreamData and EncodedStreamData ([#1309](https://github.com/maplibre/maplibre-tile-spec/pull/1309))\n- *(rust)* Coord32 cleanup, dep update ([#1304](https://github.com/maplibre/maplibre-tile-spec/pull/1304))\n- Add offline docs.rs-style workspace docs check to Rust CI and fix surfaced rustdoc links ([#1295](https://github.com/maplibre/maplibre-tile-spec/pull/1295))\n\n## [0.1.9](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.8...rust-mlt-v0.1.9) - 2026-04-13\n\n### Added\n\n- *(rust)* mlt convert support for full mbtiles conversion ([#1259](https://github.com/maplibre/maplibre-tile-spec/pull/1259))\n- *(rust)* mlt convert, encoder ([#1240](https://github.com/maplibre/maplibre-tile-spec/pull/1240))\n\n### Other\n\n- *(rust)* remove a few bad performance choices (+6% encoder gain) ([#1287](https://github.com/maplibre/maplibre-tile-spec/pull/1287))\n- benchmark and fix various performance issues in the encoder ([#1273](https://github.com/maplibre/maplibre-tile-spec/pull/1273))\n- *(rust)* Hotpath based profiling ([#1269](https://github.com/maplibre/maplibre-tile-spec/pull/1269))\n- *(rust)* massive rewrite of the encoder ([#1254](https://github.com/maplibre/maplibre-tile-spec/pull/1254))\n- *(rust)* consolidate utils ([#1248](https://github.com/maplibre/maplibre-tile-spec/pull/1248))\n- *(rust)* move frames/v01 -> decoder, adj use ([#1246](https://github.com/maplibre/maplibre-tile-spec/pull/1246))\n- *(rust)* move more code to encoder ([#1245](https://github.com/maplibre/maplibre-tile-spec/pull/1245))\n- *(rust)* mv tessellation to core ([#1220](https://github.com/maplibre/maplibre-tile-spec/pull/1220))\n- *(rust)* improve synthetics, geojson ([#1210](https://github.com/maplibre/maplibre-tile-spec/pull/1210))\n- add large FastPFOR synthetics ([#1205](https://github.com/maplibre/maplibre-tile-spec/pull/1205))\n- *(rust)* implement feature/property iterator and more type state ([#1198](https://github.com/maplibre/maplibre-tile-spec/pull/1198))\n- *(rust)* type state to represent fully-decoded layers ([#1171](https://github.com/maplibre/maplibre-tile-spec/pull/1171))\n\n## [0.1.8](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.7...rust-mlt-v0.1.8) - 2026-03-23\n\n### Other\n\n- *(rust)* migrate to Rust fastpfor ([#1190](https://github.com/maplibre/maplibre-tile-spec/pull/1190))\n\n## [0.1.7](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.6...rust-mlt-v0.1.7) - 2026-03-17\n\n### Other\n\n- *(rust)* memory budgeting, codecs  ([#1168](https://github.com/maplibre/maplibre-tile-spec/pull/1168))\n- *(rust)* minor noop cleanup ([#1167](https://github.com/maplibre/maplibre-tile-spec/pull/1167))\n- *(rust)* rename EncDec variants ([#1165](https://github.com/maplibre/maplibre-tile-spec/pull/1165))\n- *(rust)* add stateful decoder ([#1163](https://github.com/maplibre/maplibre-tile-spec/pull/1163))\n- *(rust)* get rid of borrowme, add EncDec enum ([#1141](https://github.com/maplibre/maplibre-tile-spec/pull/1141))\n\n## [0.1.6](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.5...rust-mlt-v0.1.6) - 2026-03-10\n\n### Fixed\n\n- *(rust)* migrate part of our lengths from usize to u32 ([#1078](https://github.com/maplibre/maplibre-tile-spec/pull/1078))\n\n### Other\n\n- *(rust)* rework our internal data model ([#1099](https://github.com/maplibre/maplibre-tile-spec/pull/1099))\n\n## [0.1.5](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.4...rust-mlt-v0.1.5) - 2026-03-08\n\n### Other\n\n- *(rust)* bump fastpfor, enable SIMD test, cleanup ([#1052](https://github.com/maplibre/maplibre-tile-spec/pull/1052))\n\n## [0.1.4](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-v0.1.3...rust-mlt-v0.1.4) - 2026-03-04\n\n### Fixed\n\n- *(rust)* LineString and MultiLineString geometries ([#989](https://github.com/maplibre/maplibre-tile-spec/pull/989))\n\n### Other\n\n- *(rust)* rename IntEncoder and add IntEncoding ([#1019](https://github.com/maplibre/maplibre-tile-spec/pull/1019))\n- rename `Encoder` -> `IntegerEncoder` ([#1010](https://github.com/maplibre/maplibre-tile-spec/pull/1010))\n- *(synthetic)* add Morton fixture synthetic test ([#960](https://github.com/maplibre/maplibre-tile-spec/pull/960))\n- *(rust)* rm json5, inf floats to string ([#1000](https://github.com/maplibre/maplibre-tile-spec/pull/1000))\n\n## [0.1.3](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-v0.1.2...mlt-v0.1.3) - 2026-02-25\n\n### Fixed\n\n- *(rust)* bugs in mlt UI visualizer ([#970](https://github.com/maplibre/maplibre-tile-spec/pull/970))\n\n## [0.1.2](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-v0.1.1...mlt-v0.1.2) - 2026-02-25\n\n### Added\n\n- *(rust)* mlt ls wildcards, exclusions, throttle hover redraws, dependencies ([#967](https://github.com/maplibre/maplibre-tile-spec/pull/967))\n- *(rust)* validate mlt-json with ls command ([#963](https://github.com/maplibre/maplibre-tile-spec/pull/963))\n\n### Other\n\n- *(rust)* fix another CI typo ([#959](https://github.com/maplibre/maplibre-tile-spec/pull/959))\n\n## [0.1.1](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-v0.1.0...mlt-v0.1.1) - 2026-02-25\n\n### Added\n\n- *(rust)* add tile preview in cli dir view ([#943](https://github.com/maplibre/maplibre-tile-spec/pull/943))\n\n### Other\n\n- *(rust)* more renames ([#945](https://github.com/maplibre/maplibre-tile-spec/pull/945))\n- *(rust)* rename encoding-related types for clarity ([#944](https://github.com/maplibre/maplibre-tile-spec/pull/944))\n- *(rust)* optimize some UI code ([#939](https://github.com/maplibre/maplibre-tile-spec/pull/939))\n- *(rust)* rename LogicalDecoder and PhysicalDecoder to codecs ([#923](https://github.com/maplibre/maplibre-tile-spec/pull/923))\n- *(rust)* add basic benchmark infrastructure ([#912](https://github.com/maplibre/maplibre-tile-spec/pull/912))\n- *(rust)* more cleanups ([#914](https://github.com/maplibre/maplibre-tile-spec/pull/914))\n- *(ci)* cleanup ci workflows ([#908](https://github.com/maplibre/maplibre-tile-spec/pull/908))\n"
  },
  {
    "path": "rust/mlt/Cargo.toml",
    "content": "[package]\nname = \"mlt\"\ndescription = \"MapLibre Tile Tools\"\nversion = \"0.1.11\"\nauthors.workspace = true\ncategories.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[package.metadata.binstall]\npkg-url = \"{ repo }/releases/download/rust-{ name }-v{ version }/{ name }-{ target }{ archive-suffix }\"\npkg-fmt = \"tgz\"\n\n[package.metadata.binstall.overrides.'cfg(target_os = \"windows\")']\npkg-fmt = \"zip\"\n\n[features]\n__hotpath = [\"hotpath/hotpath\", \"hotpath/tokio\", \"hotpath/threads\", \"hotpath/tui\"]\n__hotpath-alloc = [\"__hotpath\", \"hotpath/hotpath-alloc\"]\n__hotpath-mcp = [\"__hotpath\", \"hotpath/hotpath-mcp\", \"hotpath/hotpath-mcp-meta\"]\n\n[dependencies]\nanyhow.workspace = true\nbytes.workspace = true\nclap.workspace = true\ncrossterm.workspace = true\nflate2.workspace = true\nfutures.workspace = true\nglob.workspace = true\nglobset.workspace = true\nhotpath.workspace = true\nindicatif.workspace = true\nmartin-tile-utils.workspace = true\nmbtiles.workspace = true\nmlt-core.workspace = true\nmoka.workspace = true\nratatui.workspace = true\nrayon.workspace = true\nrstar.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nsize_format.workspace = true\nsqlx.workspace = true\ntabled.workspace = true\nthousands.workspace = true\ntokio.workspace = true\nwalkdir.workspace = true\nxxhash-rust.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "rust/mlt/README.md",
    "content": "## CLI Tool\n\nThe `mlt` binary provides several commands for working with MLT files:\n\n### Commands\n\n* **`dump`** - Parse an MLT file and dump raw layer data without decoding\n* **`decode`** - Parse an MLT file, decode all layers, and dump the result (supports text and `GeoJSON` output)\n* **`ui`** - Interactive terminal visualizer for MLT files\n\n### Visualizer\n\nThe visualizer command provides an interactive terminal-based UI for exploring MLT files:\n\n```bash\n# Visualize a single MLT file\ncargo run -- ui path/to/file.mlt\n\n# Browse and visualize all MLT files in a directory (recursive)\ncargo run -- ui path/to/directory\n```\n\n**Directory Mode**:\n- Lists all `.mlt` files found recursively in the directory\n- Use `↑`/`↓` to navigate the file list\n- Press `Enter` to open and visualize a file\n- Press `Esc` to go back to file list\n- Press `q` to quit\n\nFeatures:\n- **Tree View Panel (left)**: Browse layers and features in a hierarchical tree\n- \"All Layers\" - shows all features from all layers\n  - Individual layers - shows all features in that layer\n  - Individual features - shows only the selected feature\n  - Hovered features are highlighted with underlined green text\n- **Map Panel (right)**: Visual representation of the geometries\n  - Shows the extent boundary as a thin gray rectangle\n  - **Color coding by geometry type**:\n    - Points: Magenta (multipoint: light magenta)\n    - `LineStrings`: Cyan (multi-linestring: light cyan)\n    - Polygons: Blue/Red based on winding order (multi-polygon: same)\n  - **Polygon winding order visualization**:\n    - Blue: Counter-clockwise rings (typically outer rings)\n    - Red: Clockwise rings (typically holes)\n  - Selected features: Yellow\n  - Hovered features: White\n  - Automatically adjusts bounds to fit all visible geometries\n- **Mouse Interaction**:\n  - Hover over geometries to highlight them in the tree view\n- **Keyboard Navigation**:\n  - `↑`/`k` - Move selection up\n  - `↓`/`j` - Move selection down\n  - `Enter` - In layer overview mode, switch to detail mode; In file browser, open selected file\n  - `Esc` - Go back (detail → overview → file list) or quit if at top level\n  - `q` - Quit the visualizer\n"
  },
  {
    "path": "rust/mlt/src/convert/files.rs",
    "content": "use std::ffi::OsStr;\nuse std::fs;\nuse std::path::Path;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};\nuse std::time::Duration;\n\nuse anyhow::{Context as _, Result as AnyResult, anyhow, bail};\nuse indicatif::{ProgressBar, ProgressStyle};\nuse moka::sync::Cache;\nuse rayon::iter::{ParallelBridge as _, ParallelIterator as _};\nuse size_format::SizeFormatterSI;\nuse walkdir::WalkDir;\nuse xxhash_rust::xxh3::xxh3_128;\n\nuse super::{EncoderConfig, convert_mlt_buffer, convert_mvt_buffer, whole_rate_per_sec};\nuse crate::ls::is_mlt_extension;\n\n/// Only tiles below this size are tracked in the dedup cache, because\n/// larger tiles almost never repeat across a tileset.\nconst MAX_TILE_TRACK_SIZE: usize = 1024;\n\nconst CACHE_MAX_BYTES: u64 = 512 * 1024 * 1024;\n\ntype EncodedCache = Cache<u128, Arc<Vec<u8>>>;\n\nfn make_cache(max_bytes: u64) -> EncodedCache {\n    Cache::builder()\n        .max_capacity(max_bytes)\n        .weigher(|_key, value: &Arc<Vec<u8>>| u32::try_from(value.len()).unwrap_or(u32::MAX))\n        .build()\n}\n\n#[derive(Default)]\nstruct DedupStats {\n    hits: AtomicU64,\n    encoded: AtomicU64,\n    bytes_saved: AtomicU64,\n}\n\nimpl DedupStats {\n    fn record_hit(&self, size: usize) {\n        self.hits.fetch_add(1, Ordering::Relaxed);\n        self.bytes_saved.fetch_add(size as u64, Ordering::Relaxed);\n    }\n    fn record_encode(&self) {\n        self.encoded.fetch_add(1, Ordering::Relaxed);\n    }\n}\n\n#[expect(\n    clippy::cast_precision_loss,\n    reason = \"hit/miss counts are well below 2^52 for realistic tilesets\"\n)]\nfn format_dedup_line(stats: &DedupStats, cache: &EncodedCache) -> String {\n    cache.run_pending_tasks();\n    let hits = stats.hits.load(Ordering::Relaxed);\n    let encoded = stats.encoded.load(Ordering::Relaxed);\n    let bytes_saved = stats.bytes_saved.load(Ordering::Relaxed);\n    let total = hits + encoded;\n    let hit_rate = if total == 0 {\n        0.0\n    } else {\n        (hits as f64 * 100.0) / (total as f64)\n    };\n    format!(\n        \"  dedup: {encoded} unique encoded, {hits} cached ({hit_rate:.1}% hit rate, \\\n         ~{:.1}B of encode work skipped); cache weight {:.1}B\",\n        SizeFormatterSI::new(bytes_saved),\n        SizeFormatterSI::new(cache.weighted_size()),\n    )\n}\n\nfn is_convert_extension(path: &Path) -> bool {\n    matches!(\n        path.extension().and_then(OsStr::to_str),\n        Some(\"mlt\" | \"mvt\")\n    )\n}\n\npub fn convert_files(input: &Path, output: &Path, cfg: EncoderConfig) -> AnyResult<()> {\n    // For a single file, use the parent so `strip_prefix` yields just the filename.\n    let base = if input.is_dir() {\n        input\n    } else {\n        input.parent().unwrap_or(Path::new(\".\"))\n    };\n\n    let cache: EncodedCache = make_cache(CACHE_MAX_BYTES);\n    let stats = DedupStats::default();\n    let failed = AtomicUsize::new(0);\n\n    let bar = ProgressBar::new_spinner();\n    bar.set_style(\n        ProgressStyle::default_spinner()\n            .template(\"{spinner} {elapsed_precise} [{pos} files, {rate}] {msg}\")\n            .expect(\"invalid spinner template\")\n            .with_key(\"rate\", whole_rate_per_sec),\n    );\n    bar.enable_steady_tick(Duration::from_millis(100));\n\n    // `bar.println` is a no-op when hidden (non-TTY), so fall back to stderr.\n    let emit = |msg: String| {\n        if bar.is_hidden() {\n            eprintln!(\"{msg}\");\n        } else {\n            bar.println(msg);\n        }\n    };\n\n    WalkDir::new(input)\n        .into_iter()\n        .filter_map(|r| match r {\n            Ok(e) => Some(e),\n            Err(e) => {\n                emit(format!(\"warning: walkdir: {e}\"));\n                failed.fetch_add(1, Ordering::Relaxed);\n                None\n            }\n        })\n        .filter(|e| e.file_type().is_file() && is_convert_extension(e.path()))\n        .par_bridge()\n        .for_each(|entry| {\n            let in_path = entry.into_path();\n            let result = convert_file(&in_path, base, output, cfg, &cache, &stats);\n            bar.inc(1);\n            if let Err(e) = result {\n                emit(format!(\"error: {}: {e:#}\", in_path.display()));\n                failed.fetch_add(1, Ordering::Relaxed);\n            }\n        });\n\n    bar.finish_and_clear();\n\n    let n = failed.into_inner();\n    if n > 0 {\n        bail!(\"{n} file(s) failed to convert\");\n    }\n\n    let processed = stats.hits.load(Ordering::Relaxed) + stats.encoded.load(Ordering::Relaxed);\n    if processed == 0 {\n        eprintln!(\"No .mlt or .mvt files found in {}\", input.display());\n        return Ok(());\n    }\n    eprintln!(\"{}\", format_dedup_line(&stats, &cache));\n    Ok(())\n}\n\nfn convert_file(\n    file: &Path,\n    base: &Path,\n    output: &Path,\n    cfg: EncoderConfig,\n    cache: &EncodedCache,\n    stats: &DedupStats,\n) -> AnyResult<()> {\n    let rel = file\n        .strip_prefix(base)\n        .with_context(|| format!(\"stripping prefix from {}\", file.display()))?;\n    let out_path = output.join(rel).with_extension(\"mlt\");\n\n    if let Some(parent) = out_path.parent() {\n        fs::create_dir_all(parent)\n            .with_context(|| format!(\"creating directory {}\", parent.display()))?;\n    }\n\n    let buffer = fs::read(file).with_context(|| format!(\"reading {}\", file.display()))?;\n    let is_mlt = is_mlt_extension(file);\n    let file_display = file.display().to_string();\n\n    if buffer.len() > MAX_TILE_TRACK_SIZE {\n        let out_bytes = if is_mlt {\n            convert_mlt_buffer(&buffer, cfg)\n                .with_context(|| format!(\"converting MLT {file_display}\"))?\n        } else {\n            convert_mvt_buffer(buffer, cfg)\n                .with_context(|| format!(\"converting MVT {file_display}\"))?\n        };\n        stats.record_encode();\n        fs::write(&out_path, &out_bytes)\n            .with_context(|| format!(\"writing {}\", out_path.display()))?;\n        return Ok(());\n    }\n\n    let key = xxh3_128(&buffer);\n    let entry = cache\n        .entry(key)\n        .or_try_insert_with(|| -> AnyResult<Arc<Vec<u8>>> {\n            let out_bytes = if is_mlt {\n                convert_mlt_buffer(&buffer, cfg)\n                    .with_context(|| format!(\"converting MLT {file_display}\"))?\n            } else {\n                convert_mvt_buffer(buffer, cfg)\n                    .with_context(|| format!(\"converting MVT {file_display}\"))?\n            };\n            Ok(Arc::new(out_bytes))\n        })\n        .map_err(|e: Arc<anyhow::Error>| anyhow!(\"{e:#}\"))?;\n\n    let is_fresh = entry.is_fresh();\n    let out_arc = entry.into_value();\n    if is_fresh {\n        stats.record_encode();\n    } else {\n        stats.record_hit(out_arc.len());\n    }\n\n    fs::write(&out_path, out_arc.as_slice())\n        .with_context(|| format!(\"writing {}\", out_path.display()))?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt/src/convert/mod.rs",
    "content": "mod files;\nmod tileset;\n\nuse std::path::{Path, PathBuf};\n\nuse anyhow::{Result as AnyResult, bail};\nuse bytes::Bytes;\nuse clap::{Args, ValueEnum};\nuse indicatif::ProgressState;\nuse martin_tile_utils::{Encoding, decode_brotli, decode_gzip, decode_zlib, decode_zstd};\nuse mbtiles::{MbtType, NormalizedSchema};\nuse mlt_core::encoder::{EncodedUnknown, Encoder, EncoderConfig};\nuse mlt_core::mvt::mvt_to_tile_layers;\nuse mlt_core::{Decoder, Layer, Parser};\n\n#[expect(\n    clippy::cast_possible_truncation,\n    clippy::cast_sign_loss,\n    reason = \"state.per_sec() is always non-negative and well below 2^63 tiles/sec\"\n)]\nfn whole_rate_per_sec(state: &ProgressState, w: &mut dyn std::fmt::Write) {\n    let _ = w.write_fmt(format_args!(\"{}/s\", state.per_sec() as u64));\n}\n\n/// CLI-facing subset of [`MbtType`] (hides the `hash_view` detail).\n#[derive(Clone, Default, ValueEnum)]\nenum MbtFormat {\n    /// Single table with all tiles; no deduplication (smallest overhead)\n    #[default]\n    Flat,\n    /// Single table with tiles and `MD5` hashes\n    #[value(name = \"flat-with-hash\")]\n    FlatWithHash,\n    /// Separate `images` / `map` tables; identical tiles stored only once\n    Normalized,\n}\n\nimpl From<MbtFormat> for MbtType {\n    fn from(f: MbtFormat) -> Self {\n        match f {\n            MbtFormat::Flat => Self::Flat,\n            MbtFormat::FlatWithHash => Self::FlatWithHash,\n            MbtFormat::Normalized => Self::Normalized {\n                hash_view: true,\n                schema: NormalizedSchema::DedupId,\n            },\n        }\n    }\n}\n\n#[derive(Clone, Default, ValueEnum)]\nenum SortMode {\n    /// Try all sort strategies and keep the smallest result\n    #[default]\n    Auto,\n    /// Do not reorder features (original order only)\n    None,\n    /// Only try Z-order (Morton) curve sort\n    Morton,\n    /// Only try Hilbert curve sort\n    Hilbert,\n    /// Only try feature-ID ascending sort\n    Id,\n}\n\n#[derive(Args)]\npub struct ConvertArgs {\n    /// Input: a directory with .mlt/.mvt tiles, a single tile file, or an .mbtiles database\n    input: PathBuf,\n    /// Output: a directory for re-encoded .mlt files, or an .mbtiles database (required when input is .mbtiles)\n    output: PathBuf,\n    /// Add tessellation\n    #[clap(short, long, default_value = \"false\")]\n    tessellate: bool,\n    /// Sort strategy to try when re-encoding (encoder keeps the smallest result)\n    #[clap(long, default_value = \"auto\")]\n    sort: SortMode,\n    /// Schema type for the output `.mbtiles` file; defaults to the input file's schema\n    #[clap(long)]\n    mbtiles_format: Option<MbtFormat>,\n    /// Disable grouping of similar string columns into shared dictionaries\n    #[clap(long, default_value = \"false\")]\n    no_shared_dict: bool,\n}\n\npub fn convert(args: &ConvertArgs) -> AnyResult<()> {\n    let cfg = EncoderConfig {\n        tessellate: args.tessellate,\n        try_spatial_morton_sort: matches!(args.sort, SortMode::Auto | SortMode::Morton),\n        try_spatial_hilbert_sort: matches!(args.sort, SortMode::Auto | SortMode::Hilbert),\n        try_id_sort: matches!(args.sort, SortMode::Auto | SortMode::Id),\n        allow_shared_dict: !args.no_shared_dict,\n        ..Default::default()\n    };\n\n    if is_mbtiles_extension(&args.input) {\n        if !is_mbtiles_extension(&args.output) {\n            bail!(\n                \"Output must be an .mbtiles file when input is an .mbtiles file, got: {}\",\n                args.output.display()\n            );\n        }\n        if args.output.exists() {\n            bail!(\n                \"Output {} already exists; refusing to append. \\\n                 Delete it first or choose a different path.\",\n                args.output.display()\n            );\n        }\n        return tokio::runtime::Builder::new_current_thread()\n            .enable_io()\n            .enable_time()\n            .build()?\n            .block_on(tileset::convert_mbtiles(\n                &args.input,\n                &args.output,\n                args.mbtiles_format.clone(),\n                cfg,\n            ));\n    }\n\n    files::convert_files(&args.input, &args.output, cfg)\n}\n\nfn is_mbtiles_extension(path: &Path) -> bool {\n    matches!(\n        path.extension().and_then(std::ffi::OsStr::to_str),\n        Some(\"mbtiles\")\n    )\n}\n\nfn convert_mlt_buffer(buffer: &[u8], cfg: EncoderConfig) -> AnyResult<Vec<u8>> {\n    let layers = Parser::default().parse_layers(buffer)?;\n    let mut dec = Decoder::default();\n    let mut out: Vec<u8> = Vec::new();\n\n    for layer in layers {\n        match layer {\n            Layer::Tag01(l) => {\n                let tile = l.into_tile(&mut dec)?;\n                out.extend_from_slice(&tile.encode(cfg)?);\n            }\n            Layer::Unknown(u) => {\n                out.extend(EncodedUnknown::from(u).write_to(Encoder::default())?.data);\n            }\n            _ => {}\n        }\n    }\n\n    Ok(out)\n}\n\nfn convert_mvt_buffer(buffer: Vec<u8>, cfg: EncoderConfig) -> AnyResult<Vec<u8>> {\n    let mut out: Vec<u8> = Vec::new();\n    for tile in mvt_to_tile_layers(buffer)? {\n        out.extend_from_slice(&tile.encode(cfg)?);\n    }\n    Ok(out)\n}\n\nfn encode_one(data: Vec<u8>, encoding: Encoding, cfg: EncoderConfig) -> AnyResult<Bytes> {\n    let mvt = match encoding {\n        Encoding::Gzip => decode_gzip(&data)?,\n        Encoding::Zlib => decode_zlib(&data)?,\n        Encoding::Brotli => decode_brotli(&data)?,\n        Encoding::Zstd => decode_zstd(&data)?,\n        Encoding::Uncompressed | Encoding::Internal => data,\n    };\n    convert_mvt_buffer(mvt, cfg).map(Bytes::from_owner)\n}\n"
  },
  {
    "path": "rust/mlt/src/convert/tileset.rs",
    "content": "use std::path::Path;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::time::{Duration, Instant};\n\nuse anyhow::{Result as AnyResult, anyhow, bail};\nuse indicatif::{ProgressBar, ProgressStyle};\nuse martin_tile_utils::Format;\nuse mbtiles::{Mbtiles, MbtilesTranscoder};\nuse mlt_core::encoder::EncoderConfig;\nuse size_format::SizeFormatterSI;\n\nuse super::{MbtFormat, encode_one, whole_rate_per_sec};\n\n#[derive(Default)]\nstruct EncodeSizes {\n    bytes_in: AtomicU64,\n    bytes_out: AtomicU64,\n}\n\npub async fn convert_mbtiles(\n    input: &Path,\n    output: &Path,\n    mbtiles_format: Option<MbtFormat>,\n    cfg: EncoderConfig,\n) -> AnyResult<()> {\n    let src = Mbtiles::new(input)?;\n    let mut src_conn = src.open_readonly().await?;\n\n    let meta = src.get_metadata(&mut src_conn).await?;\n    let tile_info = src\n        .detect_format(&meta.tilejson, &mut src_conn)\n        .await?\n        .ok_or_else(|| anyhow!(\"{} appears to be empty\", input.display()))?;\n\n    if tile_info.format != Format::Mvt {\n        bail!(\n            \"Expected MVT tiles, got {} in {}\",\n            tile_info.format,\n            input.display()\n        );\n    }\n\n    let src_type = src.detect_type(&mut src_conn).await?;\n    let mbt_type = mbtiles_format.map_or(src_type, Into::into);\n    let encoding = tile_info.encoding;\n\n    let count_table = match src_type.normalized_schema() {\n        Some(schema) => schema.content_table(),\n        None if matches!(src_type, mbtiles::MbtType::FlatWithHash) => \"tiles_with_hash\",\n        None => \"tiles\",\n    };\n    #[expect(clippy::cast_sign_loss, reason = \"COUNT(*) is always non-negative\")]\n    let total: u64 = sqlx::query_scalar::<_, i64>(&format!(\"SELECT COUNT(*) FROM {count_table}\"))\n        .fetch_one(&mut src_conn)\n        .await? as u64;\n\n    // The transcoder opens its own connection.\n    drop(src_conn);\n\n    eprintln!(\"{} → {} ({mbt_type}):\", input.display(), output.display());\n\n    let start = Instant::now();\n    let bar = ProgressBar::new(total);\n    bar.set_style(\n        ProgressStyle::default_bar()\n            .template(\"  {bar:40.cyan/blue} {pos}/{len} tiles [{rate}, eta {eta}]\")\n            .expect(\"invalid bar template\")\n            .with_key(\"rate\", whole_rate_per_sec),\n    );\n    bar.enable_steady_tick(Duration::from_millis(200));\n\n    let bar_ref = bar.clone();\n    let sizes = Arc::new(EncodeSizes::default());\n    let sizes_ref = Arc::clone(&sizes);\n\n    let mut transcoder = MbtilesTranscoder::new(input, output, move |data| {\n        sizes_ref\n            .bytes_in\n            .fetch_add(data.len() as u64, Ordering::Relaxed);\n        let result = encode_one(data, encoding, cfg)\n            .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() });\n        if let Ok(ref encoded) = result {\n            sizes_ref\n                .bytes_out\n                .fetch_add(encoded.len() as u64, Ordering::Relaxed);\n        }\n        bar_ref.inc(1);\n        result\n    })\n    .batch_size(500)\n    .cache_max_bytes(512 * 1024 * 1024)\n    .max_tile_track_size(1024)\n    .copy_metadata(true)\n    .channel_buffer(4);\n    if mbt_type != src_type {\n        transcoder = transcoder.dst_type(mbt_type);\n    }\n\n    let stats = transcoder.run().await?;\n\n    bar.finish_and_clear();\n\n    // The transcoder copies source metadata; override `format` to MLT.\n    let dst = Mbtiles::new(output)?;\n    let mut dst_conn = dst.open_or_new().await?;\n    dst.set_metadata_value(&mut dst_conn, \"format\", Format::Mlt.metadata_format_value())\n        .await?;\n\n    let in_bytes = sizes.bytes_in.load(Ordering::Relaxed);\n    let out_bytes = sizes.bytes_out.load(Ordering::Relaxed);\n    eprintln!(\n        \"  converted {} tiles ({} unique encoded, {} cache hits, {:.1}B → {:.1}B) in {:.1?}\",\n        stats.tiles_written,\n        stats.cache_encoded,\n        stats.cache_hits,\n        SizeFormatterSI::new(in_bytes),\n        SizeFormatterSI::new(out_bytes),\n        start.elapsed(),\n    );\n\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt/src/dump.rs",
    "content": "use std::fs;\nuse std::path::PathBuf;\n\nuse anyhow::{Result as AnyResult, bail};\nuse clap::Args;\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::{Decoder, MltResult, Parser};\n\nuse crate::OutputFormat;\nuse crate::ls::is_mlt_extension;\n\n#[derive(Args)]\npub struct DumpArgs {\n    /// Path to a tile file (.mlt, .mvt, .pbf)\n    file: PathBuf,\n\n    /// Output format\n    #[arg(short, long, default_value_t, value_enum)]\n    format: OutputFormat,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub enum AfterDump {\n    KeepRaw,\n    Decode,\n}\n\npub fn dump(args: &DumpArgs, decode: AfterDump) -> AnyResult<()> {\n    let buffer = fs::read(&args.file)?;\n\n    if is_mlt_extension(&args.file) {\n        dump_mlt(args, decode, &buffer)?;\n    } else {\n        dump_mvt(args, buffer)?;\n    }\n    Ok(())\n}\n\nfn dump_mlt(args: &DumpArgs, decode: AfterDump, buffer: &[u8]) -> AnyResult<()> {\n    let layers = Parser::default().parse_layers(buffer)?;\n\n    match args.format {\n        OutputFormat::Text => match decode {\n            AfterDump::KeepRaw => {\n                for (i, layer) in layers.into_iter().enumerate() {\n                    println!(\"=== Layer {i} ===\");\n                    println!(\"{layer:#?}\");\n                }\n            }\n            AfterDump::Decode => {\n                let layers = Decoder::default().decode_all(layers)?;\n                for (i, layer) in layers.into_iter().enumerate() {\n                    println!(\"=== Layer {i} ===\");\n                    println!(\"{layer:#?}\");\n                }\n            }\n        },\n        OutputFormat::GeoJson => {\n            if decode == AfterDump::KeepRaw {\n                bail!(\"GeoJSON output only works with `mlt decode`\");\n            }\n            let fc = FeatureCollection::from_layers(Decoder::default().decode_all(layers)?)?;\n            println!(\"{}\", serde_json::to_string_pretty(&fc)?);\n        }\n    }\n    Ok(())\n}\n\nfn dump_mvt(args: &DumpArgs, buffer: Vec<u8>) -> MltResult<()> {\n    let fc = mlt_core::mvt::mvt_to_feature_collection(buffer)?;\n    match args.format {\n        OutputFormat::Text => {\n            for (i, feature) in fc.features.iter().enumerate() {\n                println!(\"=== Feature {i} ===\");\n                println!(\"{feature:#?}\");\n            }\n        }\n        OutputFormat::GeoJson => {\n            println!(\"{}\", serde_json::to_string_pretty(&fc)?);\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt/src/ls.rs",
    "content": "use std::collections::HashSet;\nuse std::ffi::OsStr;\nuse std::fs;\nuse std::io::Write as _;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr as _;\nuse std::string::ToString;\n\nuse anyhow::Result as AnyResult;\nuse clap::{Args, ValueEnum};\nuse flate2::Compression;\nuse flate2::write::GzEncoder;\nuse globset::{GlobSet, GlobSetBuilder};\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::mvt::mvt_to_feature_collection;\nuse mlt_core::wire::StatType::{DecodedDataSize, DecodedMetaSize, FeatureCount};\nuse mlt_core::wire::{\n    Analyze as _, DictionaryType, LengthType, LogicalEncoding, OffsetType, PhysicalEncoding,\n    StreamMeta, StreamType,\n};\nuse mlt_core::{Decoder, GeometryType, Parser};\nuse rayon::iter::{IntoParallelRefIterator as _, ParallelIterator as _};\nuse serde::Serialize;\nuse size_format::SizeFormatterSI;\nuse tabled::Table;\nuse tabled::builder::Builder;\nuse tabled::settings::object::{Cell, Columns};\nuse tabled::settings::span::ColumnSpan;\nuse tabled::settings::style::HorizontalLine;\nuse tabled::settings::{Alignment, Style};\nuse thousands::Separable as _;\n\n#[derive(Debug, Args)]\npub struct LsArgs {\n    /// Paths to tile files (.mlt, .mvt, .pbf) or directories\n    #[arg(required = true)]\n    paths: Vec<PathBuf>,\n\n    /// Filter by file extension (e.g. mlt, mvt, pbf). Can be specified multiple times.\n    #[arg(short = 'e', long)]\n    extension: Vec<String>,\n\n    /// Exclude paths matching the given glob (e.g. \"**/fixtures/**\", \"**/*.pbf\"). Can be specified multiple times.\n    #[arg(short = 'E', long = \"exclude\")]\n    exclude: Vec<String>,\n\n    /// Disable recursive directory traversal\n    #[arg(long)]\n    no_recursive: bool,\n\n    /// Level of detail to show (can be specified multiple times for more details)\n    #[arg(short, long, value_enum, default_values = [\"basic\", \"gzip\"])]\n    details: Vec<Detail>,\n\n    /// Output format (table or JSON)\n    #[arg(short, long, default_value = \"table\", value_enum)]\n    format: LsFormat,\n\n    /// Validate tile files against JSON validation files in the same directory (with .json extension)\n    #[arg(long)]\n    validate_to_json: bool,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]\npub enum Detail {\n    /// Show basic statistics: file size, encoding %, layers, features\n    Basic,\n    /// Show all available statistics\n    All,\n    /// Show gzip size estimation and compression ratio\n    #[clap(name = \"gzip\")]\n    GZip,\n    /// Show stream/encoding algorithms used (Algorithms column)\n    Algorithms,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\npub struct LsFlags {\n    pub gzip: bool,\n    pub algorithms: bool,\n    pub validate: bool,\n}\n\nimpl From<&LsArgs> for LsFlags {\n    fn from(args: &LsArgs) -> Self {\n        use Detail::{Algorithms, All, GZip};\n        let details = args.details.as_slice();\n        Self {\n            gzip: details.contains(&GZip) || details.contains(&All),\n            algorithms: details.contains(&Algorithms) || details.contains(&All),\n            validate: args.validate_to_json,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, ValueEnum)]\npub enum LsFormat {\n    /// Table output with aligned columns\n    Table,\n    /// JSON output\n    Json,\n}\n\n/// Compression reduction: `(1 - compressed/original) * 100`.\n/// Returns 0 if `original` is 0.\n#[expect(clippy::cast_precision_loss)]\nfn percent(compressed: usize, original: usize) -> f64 {\n    if original > 0 {\n        (1.0 - compressed as f64 / original as f64) * 100.0\n    } else {\n        0.0\n    }\n}\n\n#[expect(clippy::cast_precision_loss)]\nfn percent_of(part: usize, whole: usize) -> f64 {\n    if whole > 0 {\n        (part as f64 / whole as f64) * 100.0\n    } else {\n        0.0\n    }\n}\n\n/// Column index for file table sorting in the UI.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum FileSortColumn {\n    File,\n    Size,\n    EncPct,\n    Layers,\n    Features,\n}\n\n/// Algorithm description for a file (MLT stream combo or protobuf for MVT).\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum FileAlgorithm {\n    Mlt(StreamType, PhysicalEncoding, StatLogicalCodec),\n    Mvt,\n}\n\nimpl std::fmt::Display for FileAlgorithm {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Mvt => write!(f, \"Protobuf\"),\n            Self::Mlt(phys_type, physical, logical) => {\n                let phys_type = match phys_type {\n                    StreamType::Present => \"Present\",\n                    StreamType::Data(v) => match v {\n                        DictionaryType::None => \"RawData\",\n                        DictionaryType::Vertex => \"Vertex\",\n                        DictionaryType::Single => \"Single\",\n                        DictionaryType::Shared => \"Shared\",\n                        DictionaryType::Morton => \"Morton\",\n                        DictionaryType::Fsst => \"Fsst\",\n                    },\n                    StreamType::Offset(v) => match v {\n                        OffsetType::Vertex => \"VertexOffset\",\n                        OffsetType::Index => \"IndexOffset\",\n                        OffsetType::String => \"StringOffset\",\n                        OffsetType::Key => \"KeyOffset\",\n                    },\n                    StreamType::Length(v) => match v {\n                        LengthType::VarBinary => \"VarBinaryLen\",\n                        LengthType::Geometries => \"GeomLen\",\n                        LengthType::Parts => \"PartsLen\",\n                        LengthType::Rings => \"RingsLen\",\n                        LengthType::Triangles => \"TrianglesLen\",\n                        LengthType::Symbol => \"SymbolLen\",\n                        LengthType::Dictionary => \"DictLen\",\n                    },\n                };\n                let physical = match physical {\n                    PhysicalEncoding::None => \"\",\n                    PhysicalEncoding::FastPFor256 => \"FastPFOR\",\n                    PhysicalEncoding::VarInt => \"VarInt\",\n                };\n                let logical = match logical {\n                    StatLogicalCodec::None => \"\",\n                    StatLogicalCodec::Delta => \"Delta\",\n                    StatLogicalCodec::DeltaRle => \"DeltaRle\",\n                    StatLogicalCodec::Rle => \"Rle\",\n                    StatLogicalCodec::ComponentwiseDelta => \"CwDelta\",\n                    StatLogicalCodec::Morton => \"Morton\",\n                    StatLogicalCodec::MortonDelta => \"MortonDelta\",\n                    StatLogicalCodec::MortonRle => \"MortonRle\",\n                    StatLogicalCodec::PseudoDecimal => \"PseudoDec\",\n                };\n                write!(f, \"{phys_type}\")?;\n                if !physical.is_empty() {\n                    write!(f, \"-{physical}\")?;\n                }\n                if !logical.is_empty() {\n                    write!(f, \"-{logical}\")?;\n                }\n                Ok(())\n            }\n        }\n    }\n}\n\nimpl Serialize for FileAlgorithm {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_str(&self.to_string())\n    }\n}\n\n/// Dash shown when a numeric column is not applicable (e.g. MVT has no Enc %).\npub const NA: &str = \"—\";\n\n#[must_use]\npub fn na(v: Option<String>) -> String {\n    v.unwrap_or_else(|| NA.to_string())\n}\n\n#[derive(Debug, Clone, Default, Serialize)]\npub struct MltFileInfo {\n    pub path: String,\n    pub size: usize,\n    pub encoding_pct: Option<f64>,\n    pub data_size: Option<usize>,\n    pub meta_size: Option<usize>,\n    pub meta_pct: Option<f64>,\n    pub gzipped_size: Option<usize>,\n    pub gzip_pct: Option<f64>,\n    pub layers: usize,\n    pub features: usize,\n    pub streams: Option<usize>,\n    pub algorithms: HashSet<FileAlgorithm>,\n    pub geometries: HashSet<GeometryType>,\n    pub matches_json: Option<bool>,\n}\n\nimpl MltFileInfo {}\n\nimpl MltFileInfo {\n    #[must_use]\n    pub fn geometries_display(&self) -> String {\n        geometries_display(&self.geometries)\n    }\n    #[must_use]\n    pub fn algorithms_display(&self) -> String {\n        algorithms_display(&self.algorithms)\n    }\n}\n\n#[derive(serde::Serialize, Clone)]\n#[serde(untagged)]\n#[expect(clippy::large_enum_variant)]\npub enum LsRow {\n    Info {\n        path: PathBuf,\n        info: MltFileInfo,\n    },\n    Error {\n        path: PathBuf,\n        size: Option<usize>,\n        error: String,\n    },\n    /// Placeholder while analysis is in progress\n    Loading {\n        path: PathBuf,\n    },\n}\n\nimpl LsRow {\n    /// Path for this row (file path, or path that failed/loading).\n    #[must_use]\n    pub fn path(&self) -> &Path {\n        match self {\n            Self::Info { path, .. } | Self::Error { path, .. } | Self::Loading { path } => {\n                path.as_path()\n            }\n        }\n    }\n}\n\n/// True if the path string contains glob metacharacters `\"*?[{\"`.\nfn has_glob_metachars(path: &Path) -> bool {\n    let s = path.to_string_lossy();\n    s.contains('*') || s.contains('?') || s.contains('[') || s.contains('{')\n}\n\n/// Expand path arguments: if a path contains glob metacharacters, expand it to matching paths;\n/// otherwise use the path as-is. Directories are left as-is so `collect_tile_files` can recurse into them.\nfn expand_path_args(paths: &[PathBuf]) -> AnyResult<Vec<PathBuf>> {\n    let mut out = Vec::new();\n    for path in paths {\n        if has_glob_metachars(path) {\n            for entry in glob::glob(path.to_string_lossy().as_ref())? {\n                out.push(entry?);\n            }\n        } else {\n            out.push(path.clone());\n        }\n    }\n    Ok(out)\n}\n\n/// Build a `GlobSet` from patterns; returns None if patterns is empty.\nfn build_exclude_set(patterns: &[String]) -> AnyResult<Option<GlobSet>> {\n    if patterns.is_empty() {\n        return Ok(None);\n    }\n    let mut builder = GlobSetBuilder::new();\n    for p in patterns {\n        builder.add(globset::Glob::new(p)?);\n    }\n    Ok(Some(builder.build()?))\n}\n\n/// List tile files with statistics.\n/// Returns `true` if all files were valid, `false` if any file had an error, or no files.\npub fn ls(args: &LsArgs) -> AnyResult<bool> {\n    let flags = LsFlags::from(args);\n    let mut all_files = Vec::new();\n\n    // Expand path arguments as globs when they contain *?[{; directories are left as-is and handled below.\n    let expanded_paths = expand_path_args(&args.paths)?;\n    let exclude = build_exclude_set(&args.exclude)?;\n\n    for path in &expanded_paths {\n        let files = collect_tile_files(path, args, exclude.as_ref())?;\n        all_files.extend(files);\n    }\n\n    if all_files.is_empty() {\n        eprintln!(\"No tile files found\");\n        return Ok(false);\n    }\n\n    let base_path = if args.paths.len() == 1 && !has_glob_metachars(&args.paths[0]) {\n        &args.paths[0]\n    } else {\n        Path::new(\".\")\n    };\n\n    let result = analyze_tile_files(all_files.as_slice(), base_path, flags);\n    match args.format {\n        LsFormat::Table => print_table(&result, flags),\n        LsFormat::Json => println!(\"{}\", serde_json::to_string_pretty(&result)?),\n    }\n\n    Ok(result.iter().all(|r| match r {\n        LsRow::Info {\n            info: MltFileInfo { matches_json, .. },\n            ..\n        } => matches_json.unwrap_or(true),\n        _ => false,\n    }))\n}\n\n/// Analyze tile files (MLT and MVT) and return rows (for reuse by UI).\n#[must_use]\npub fn analyze_tile_files(paths: &[PathBuf], base_path: &Path, flags: LsFlags) -> Vec<LsRow> {\n    paths\n        .par_iter()\n        .map(|path| match analyze_tile_file(path, base_path, flags) {\n            Ok(info) => LsRow::Info {\n                path: path.clone(),\n                info,\n            },\n            Err(e) => LsRow::Error {\n                path: path.clone(),\n                error: e.to_string(),\n                size: fs::metadata(path)\n                    .ok()\n                    .and_then(|m| usize::try_from(m.len()).ok()),\n            },\n        })\n        .collect()\n}\n\n/// Return cells for UI table display: [File, Size, Enc%, Layers, Features].\n#[must_use]\npub fn row_cells(row: &LsRow) -> [String; 5] {\n    let fmt_size = |n: usize| format!(\"{:.1}B\", SizeFormatterSI::new(n as u64));\n    match row {\n        LsRow::Info { info, .. } => [\n            info.path.clone(),\n            format!(\"{:>8}\", fmt_size(info.size)),\n            format!(\"{:>6}\", na(info.encoding_pct.map(fmt_pct))),\n            format!(\"{:>6}\", info.layers),\n            format!(\"{:>10}\", info.features.separate_with_commas()),\n        ],\n        LsRow::Error {\n            path,\n            error: _,\n            size,\n        } => [\n            path.display().to_string(),\n            size.map_or_else(String::new, |n| {\n                format!(\"{:>8}\", format!(\"{:.1}B\", SizeFormatterSI::new(n as u64)))\n            }),\n            String::new(),\n            String::new(),\n            String::new(),\n        ],\n        LsRow::Loading { path } => [\n            path.display().to_string(),\n            \"…\".to_string(),\n            \"…\".to_string(),\n            \"…\".to_string(),\n            \"…\".to_string(),\n        ],\n    }\n}\n\n/// Path string for UI display; when `base` is given, returns path relative to base (same as Info row display).\n#[must_use]\npub fn path_display(path: &Path, base: Option<&Path>) -> String {\n    match base {\n        None => path.display().to_string(),\n        Some(b) if b.is_file() => path\n            .file_name()\n            .and_then(|n| n.to_str())\n            .unwrap_or(\"\")\n            .to_string(),\n        Some(b) => path.strip_prefix(b).map_or_else(\n            |_| path.display().to_string(),\n            |p| p.to_string_lossy().to_string(),\n        ),\n    }\n}\n\n/// Six-column cells for UI table: [File, Size, Enc %, Layers, Features, Notes]. Uses `path_display(path, base)` for the file column. Notes column is error message for Error rows, empty otherwise.\n#[must_use]\npub fn row_cells_6(row: &LsRow, base: Option<&Path>) -> [String; 6] {\n    let cells5 = row_cells(row);\n    let file_col = path_display(row.path(), base);\n    let notes = match row {\n        LsRow::Error { error, .. } => error.clone(),\n        LsRow::Info { .. } | LsRow::Loading { .. } => String::new(),\n    };\n    [\n        file_col,\n        cells5[1].clone(),\n        cells5[2].clone(),\n        cells5[3].clone(),\n        cells5[4].clone(),\n        notes,\n    ]\n}\n\npub(crate) fn is_tile_extension(path: &Path) -> bool {\n    matches!(\n        path.extension().and_then(OsStr::to_str),\n        Some(\"mlt\" | \"mvt\" | \"pbf\")\n    )\n}\n\npub(crate) fn is_mlt_extension(path: &Path) -> bool {\n    matches!(path.extension().and_then(OsStr::to_str), Some(\"mlt\"))\n}\n\npub(crate) fn is_mbt_extension(path: &Path) -> bool {\n    matches!(path.extension().and_then(OsStr::to_str), Some(\"mbtiles\"))\n}\n\nfn matches_extension_filter(path: &Path, extensions: &[String]) -> bool {\n    let ext = path\n        .extension()\n        .and_then(OsStr::to_str)\n        .map(str::to_lowercase);\n    match ext {\n        Some(ext) => extensions\n            .iter()\n            .any(|e| e.trim_start_matches('.').to_lowercase() == ext),\n        None => false,\n    }\n}\n\nfn collect_tile_files(\n    path: &Path,\n    args: &LsArgs,\n    exclude_set: Option<&GlobSet>,\n) -> AnyResult<Vec<PathBuf>> {\n    let matches_ext = |p: &Path| {\n        if args.extension.is_empty() {\n            is_tile_extension(p)\n        } else {\n            matches_extension_filter(p, &args.extension)\n        }\n    };\n    let excluded = |p: &Path| exclude_set.is_some_and(|s| s.is_match(p));\n\n    let mut files = Vec::new();\n    if path.is_dir() {\n        collect_from_dir(\n            path,\n            &mut files,\n            !args.no_recursive,\n            &matches_ext,\n            exclude_set,\n        )?;\n    } else if path.is_file() && !excluded(path) && matches_ext(path) {\n        files.push(path.to_path_buf());\n    }\n\n    Ok(files)\n}\n\nfn collect_from_dir<F>(\n    dir: &Path,\n    files: &mut Vec<PathBuf>,\n    recursive: bool,\n    matches_ext: &F,\n    exclude_set: Option<&GlobSet>,\n) -> AnyResult<()>\nwhere\n    F: Fn(&Path) -> bool,\n{\n    for entry in fs::read_dir(dir)? {\n        let path = entry?.path();\n        if path.is_file() {\n            if !exclude_set.is_some_and(|s| s.is_match(&path)) && matches_ext(&path) {\n                files.push(path);\n            }\n        } else if recursive && path.is_dir() && !exclude_set.is_some_and(|s| s.is_match(&path)) {\n            collect_from_dir(&path, files, recursive, matches_ext, exclude_set)?;\n        }\n    }\n    Ok(())\n}\n\npub fn analyze_tile_file(path: &Path, base_path: &Path, flags: LsFlags) -> AnyResult<MltFileInfo> {\n    let buffer = fs::read(path)?;\n    let mut info = if is_mlt_extension(path) {\n        analyze_mlt_buffer(&buffer, path, flags)?\n    } else {\n        analyze_mvt_buffer(&buffer)?\n    };\n    info.path = if base_path.is_file() {\n        path.file_name()\n            .and_then(|n| n.to_str())\n            .unwrap_or(\"\")\n            .to_string()\n    } else {\n        path.strip_prefix(base_path)\n            .unwrap_or(path)\n            .to_string_lossy()\n            .to_string()\n    };\n    if flags.gzip {\n        let gzip_size = estimate_gzip_size(&buffer)?;\n        info.gzipped_size = Some(gzip_size);\n        info.gzip_pct = Some(percent(gzip_size, buffer.len()));\n    }\n    Ok(info)\n}\n\npub fn analyze_mlt_buffer(buffer: &[u8], path: &Path, flags: LsFlags) -> AnyResult<MltFileInfo> {\n    let layers = Parser::default().parse_layers(buffer)?;\n\n    let mut stream_count = 0;\n    let mut algorithms: HashSet<StreamStat> = HashSet::new();\n    for layer in &layers {\n        if let Some(layer01) = layer.as_layer01() {\n            layer01.for_each_stream(&mut |stream_meta| {\n                stream_count += 1;\n                collect_stream_info(stream_meta, &mut algorithms);\n            });\n        }\n    }\n\n    let layers = Decoder::default().decode_all(layers)?;\n\n    let mut geometries = HashSet::new();\n    let mut feature_count = 0;\n    let mut data_size = 0;\n    let mut meta_size = 0;\n\n    for layer in &layers {\n        if let Some(layer01) = layer.as_layer01() {\n            data_size += layer01.collect_statistic(DecodedDataSize);\n            meta_size += layer01.collect_statistic(DecodedMetaSize);\n            feature_count += layer01.collect_statistic(FeatureCount);\n            for &geom_type in layer01.geometry_values().vector_types() {\n                geometries.insert(geom_type);\n            }\n        }\n    }\n\n    let layer_count = layers.len();\n    let matches_json = if flags.validate {\n        let json_path = path.with_extension(\"json\");\n        if json_path.is_file() {\n            let expected = FeatureCollection::from_str(&fs::read_to_string(&json_path)?)\n                .map_err(|e| anyhow::anyhow!(\"{e}\"))?;\n            let actual = FeatureCollection::from_layers(layers)?;\n            Some(actual.equals(&expected)?)\n        } else {\n            Some(false)\n        }\n    } else {\n        None\n    };\n\n    let algorithms: HashSet<FileAlgorithm> = algorithms\n        .into_iter()\n        .map(|(a, b, c)| FileAlgorithm::Mlt(a, b, c))\n        .collect();\n\n    Ok(MltFileInfo {\n        size: buffer.len(),\n        encoding_pct: Some(percent(buffer.len(), data_size + meta_size)),\n        data_size: Some(data_size),\n        meta_size: Some(meta_size),\n        meta_pct: Some(percent_of(meta_size, data_size)),\n        layers: layer_count,\n        features: feature_count,\n        streams: Some(stream_count),\n        algorithms,\n        geometries,\n        matches_json,\n        ..MltFileInfo::default()\n    })\n}\n\nfn analyze_mvt_buffer(buffer: &[u8]) -> AnyResult<MltFileInfo> {\n    let fc = mvt_to_feature_collection(buffer.to_vec())?;\n\n    let mut layer_names = HashSet::new();\n    let mut geometries = HashSet::new();\n    for feat in &fc.features {\n        // FIXME: we shouldn't use \"magical\" properties to pass values around\n        if let Some(name) = feat.properties.get(\"_layer\").and_then(|v| v.as_str()) {\n            layer_names.insert(name.to_string());\n        }\n        if let Ok(gt) = GeometryType::try_from(&feat.geometry) {\n            geometries.insert(gt);\n        }\n    }\n\n    Ok(MltFileInfo {\n        size: buffer.len(),\n        layers: layer_names.len(),\n        features: fc.features.len(),\n        algorithms: std::iter::once(FileAlgorithm::Mvt).collect(),\n        geometries,\n        ..MltFileInfo::default()\n    })\n}\n\ntype StreamStat = (StreamType, PhysicalEncoding, StatLogicalCodec);\n\n/// Mirrors [`LogicalEncoding`] without associated metadata values.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub enum StatLogicalCodec {\n    None,\n    Delta,\n    DeltaRle,\n    ComponentwiseDelta,\n    Rle,\n    Morton,\n    MortonDelta,\n    MortonRle,\n    PseudoDecimal,\n}\n\nimpl From<LogicalEncoding> for StatLogicalCodec {\n    fn from(ld: LogicalEncoding) -> Self {\n        match ld {\n            LogicalEncoding::None => Self::None,\n            LogicalEncoding::Delta => Self::Delta,\n            LogicalEncoding::DeltaRle(_) => Self::DeltaRle,\n            LogicalEncoding::ComponentwiseDelta => Self::ComponentwiseDelta,\n            LogicalEncoding::Rle(_) => Self::Rle,\n            LogicalEncoding::Morton(_) => Self::Morton,\n            LogicalEncoding::MortonDelta(_) => Self::MortonDelta,\n            LogicalEncoding::MortonRle(_) => Self::MortonRle,\n            LogicalEncoding::PseudoDecimal => Self::PseudoDecimal,\n        }\n    }\n}\n\nfn collect_stream_info(meta: StreamMeta, algo: &mut HashSet<StreamStat>) {\n    algo.insert((\n        meta.stream_type,\n        meta.encoding.physical,\n        StatLogicalCodec::from(meta.encoding.logical),\n    ));\n}\n\nfn estimate_gzip_size(data: &[u8]) -> AnyResult<usize> {\n    let mut encoder = GzEncoder::new(Vec::new(), Compression::default());\n    encoder.write_all(data)?;\n    let compressed = encoder.finish()?;\n    Ok(compressed.len())\n}\n\nfn geometries_display(geometries: &HashSet<GeometryType>) -> String {\n    let abbrev = |g: GeometryType| match g {\n        GeometryType::Point => \"Pt\",\n        GeometryType::LineString => \"Line\",\n        GeometryType::Polygon => \"Poly\",\n        GeometryType::MultiPoint => \"MPt\",\n        GeometryType::MultiLineString => \"MLine\",\n        GeometryType::MultiPolygon => \"MPoly\",\n    };\n    let mut v: Vec<GeometryType> = geometries.iter().copied().collect();\n    v.sort_unstable();\n    v.iter().map(|g| abbrev(*g)).collect::<Vec<_>>().join(\",\")\n}\n\nfn algorithms_display(algorithms: &HashSet<FileAlgorithm>) -> String {\n    let mut v: Vec<_> = algorithms.iter().map(ToString::to_string).collect();\n    v.sort_unstable();\n    v.join(\",\")\n}\n\nfn print_table(rows: &[LsRow], flags: LsFlags) {\n    let fmt_size = |n: usize| format!(\"{:.1}B\", SizeFormatterSI::new(n as u64));\n\n    let infos: Vec<&MltFileInfo> = rows\n        .iter()\n        .filter_map(|r| match r {\n            LsRow::Info { info, .. } => Some(info),\n            LsRow::Error { .. } | LsRow::Loading { .. } => None,\n        })\n        .collect();\n    let has_total = infos.len() > 1;\n    let mut error_table_rows = Vec::new();\n    let mut builder = Builder::default();\n\n    let mut header = vec![\"File\", \"Size\", \"Enc %\", \"Decoded\", \"Meta\", \"Meta %\"];\n    if flags.gzip {\n        header.push(\"Gzipped\");\n        header.push(\"Gz %\");\n    }\n    header.extend([\"Layer\", \"Feature\", \"Stream\", \"Geometry Types\"]);\n    if flags.validate {\n        header.push(\"JSON\");\n    }\n    if flags.algorithms {\n        header.push(\"Algorithms\");\n    }\n    let num_cols = header.len();\n    builder.push_record(header);\n\n    for (i, row) in rows.iter().enumerate() {\n        match row {\n            LsRow::Info { info, .. } => {\n                if let Some(true) = info.matches_json\n                    && flags.validate\n                {\n                    continue; // When validating, no need to show valid rows\n                }\n                let mut data_row = vec![\n                    info.path.clone(),\n                    fmt_size(info.size),\n                    na(info.encoding_pct.map(fmt_pct)),\n                    na(info.data_size.map(fmt_size)),\n                    na(info.meta_size.map(fmt_size)),\n                    na(info.meta_pct.map(fmt_pct)),\n                ];\n                if flags.gzip {\n                    data_row.push(na(info.gzipped_size.map(fmt_size)));\n                    data_row.push(na(info.gzip_pct.map(fmt_pct)));\n                }\n                data_row.extend([\n                    info.layers.separate_with_commas(),\n                    info.features.separate_with_commas(),\n                    na(info.streams.map(|n| n.separate_with_commas())),\n                    info.geometries_display(),\n                ]);\n                if flags.validate {\n                    data_row.push(match info.matches_json {\n                        Some(true) => \"✓\".to_string(),\n                        Some(false) => \"✗\".to_string(),\n                        None => NA.to_string(),\n                    });\n                }\n                if flags.algorithms {\n                    data_row.push(info.algorithms_display());\n                }\n                builder.push_record(data_row);\n            }\n            LsRow::Error { path, error, size } => {\n                let size_str = size.map_or_else(String::new, &fmt_size);\n                let mut data_row = vec![\n                    path.display().to_string(),\n                    size_str,\n                    format!(\"ERROR: {error}\"),\n                ];\n                data_row.resize(num_cols, String::new());\n                builder.push_record(data_row);\n                error_table_rows.push(i + 1);\n            }\n            LsRow::Loading { .. } => unreachable!(\"Loading?\"),\n        }\n    }\n\n    if has_total {\n        let total_size: usize = infos.iter().map(|i| i.size).sum();\n        let total_data: Option<usize> = infos\n            .iter()\n            .try_fold(0usize, |acc, i| i.data_size.map(|d| acc + d));\n        let total_meta: Option<usize> = infos\n            .iter()\n            .try_fold(0usize, |acc, i| i.meta_size.map(|m| acc + m));\n        let total_gzipped: usize = infos.iter().filter_map(|i| i.gzipped_size).sum();\n        let total_layers: usize = infos.iter().map(|i| i.layers).sum();\n        let total_features: usize = infos.iter().map(|i| i.features).sum();\n        let total_streams: Option<usize> = infos\n            .iter()\n            .try_fold(0usize, |acc, i| i.streams.map(|s| acc + s));\n\n        let (enc_pct, decoded, meta, meta_pct) = match (total_data, total_meta) {\n            (Some(d), Some(m)) => (\n                fmt_pct(percent(total_size, d + m)),\n                fmt_size(d),\n                fmt_size(m),\n                fmt_pct(percent_of(m, d)),\n            ),\n            _ => (\n                NA.to_string(),\n                NA.to_string(),\n                NA.to_string(),\n                NA.to_string(),\n            ),\n        };\n        let mut row = vec![\n            \"TOTAL\".to_string(),\n            fmt_size(total_size),\n            enc_pct,\n            decoded,\n            meta,\n            meta_pct,\n        ];\n        if flags.gzip {\n            let has_any_gzip = infos.iter().any(|i| i.gzipped_size.is_some());\n            let gzip_size_str = if has_any_gzip {\n                fmt_size(total_gzipped)\n            } else {\n                NA.to_string()\n            };\n            let gzip_pct_str = if has_any_gzip {\n                fmt_pct(percent(total_gzipped, total_size))\n            } else {\n                NA.to_string()\n            };\n            row.push(gzip_size_str);\n            row.push(gzip_pct_str);\n        }\n        row.extend([\n            total_layers.separate_with_commas(),\n            total_features.separate_with_commas(),\n            na(total_streams.map(|s| s.separate_with_commas())),\n            String::new(),\n        ]);\n        if flags.validate {\n            row.push(String::new());\n        }\n        if flags.algorithms {\n            row.push(String::new());\n        }\n        builder.push_record(row);\n    }\n\n    let header_line = HorizontalLine::new('-').intersection('+');\n    let mut table = Table::from(builder);\n\n    #[expect(clippy::cast_possible_wrap)]\n    let col_span = ColumnSpan::new((num_cols - 1) as isize);\n    for &row_idx in &error_table_rows {\n        table.modify(Cell::new(row_idx, 1), col_span);\n    }\n\n    if has_total {\n        let total_row = rows.len() + 1;\n        table.with(\n            Style::empty()\n                .vertical('|')\n                .horizontals([(1, header_line), (total_row, header_line)]),\n        );\n    } else {\n        table.with(Style::empty().vertical('|').horizontals([(1, header_line)]));\n    }\n    // File - left aligned, size..stream (9-11) right, two more left\n    table.modify(\n        Columns::new(1..9 + if flags.gzip { 2 } else { 0 }),\n        Alignment::right(),\n    );\n    for &row_idx in &error_table_rows {\n        table.modify(Cell::new(row_idx, 1), Alignment::left());\n    }\n\n    println!(\"{table}\");\n}\n\nfn fmt_pct(v: f64) -> String {\n    if v.abs() >= 10.0 {\n        format!(\"{v:.0}%\")\n    } else if v.abs() >= 1.0 {\n        format!(\"{v:.1}%\")\n    } else {\n        format!(\"{v:.2}%\")\n    }\n}\n"
  },
  {
    "path": "rust/mlt/src/main.rs",
    "content": "pub mod convert;\npub mod dump;\npub mod ls;\npub mod ui;\n\nuse std::process::exit;\n\nuse anyhow::Result as AnyResult;\nuse clap::{Parser, Subcommand, ValueEnum};\n\nuse crate::convert::{ConvertArgs, convert};\nuse crate::dump::{AfterDump, DumpArgs, dump};\nuse crate::ls::{LsArgs, ls};\nuse crate::ui::{UiArgs, ui};\n\n#[hotpath::main]\nfn main() -> AnyResult<()> {\n    match Cli::parse().command {\n        Commands::Convert(args) => convert(&args)?,\n        Commands::Dump(args) => dump(&args, AfterDump::KeepRaw)?,\n        Commands::Decode(args) => dump(&args, AfterDump::Decode)?,\n        Commands::Ls(args) => {\n            if !ls(&args)? {\n                exit(1)\n            }\n        }\n        Commands::Ui(args) => ui(&args)?,\n    }\n\n    Ok(())\n}\n\n#[derive(Parser)]\n#[command(name = \"mlt\", about = \"MapLibre Tile format utilities\")]\nstruct Cli {\n    #[command(subcommand)]\n    command: Commands,\n}\n\n#[derive(Subcommand)]\nenum Commands {\n    /// Convert .mlt and .mvt tiles in a directory tree to re-encoded .mlt files\n    Convert(ConvertArgs),\n    /// Parse a tile file (.mlt, .mvt, .pbf) and dump raw layer data without decoding\n    Dump(DumpArgs),\n    /// Parse a tile file (.mlt, .mvt, .pbf), decode all layers, and dump the result\n    Decode(DumpArgs),\n    /// List tile files with statistics\n    Ls(LsArgs),\n    /// Visualize a tile file (.mlt, .mvt, .pbf) in an interactive TUI\n    Ui(UiArgs),\n}\n\n#[derive(Clone, Default, ValueEnum)]\nenum OutputFormat {\n    /// Human-readable text output\n    #[default]\n    Text,\n    /// `GeoJSON` output\n    #[clap(alias = \"geojson\")]\n    GeoJson,\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/mbt.rs",
    "content": "//! `MBTiles` map viewer state and tile loading.\n\nuse std::collections::{HashMap, HashSet};\nuse std::path::PathBuf;\nuse std::sync::mpsc::{self, TryRecvError};\nuse std::thread;\n\nuse martin_tile_utils::{decode_gzip, decode_zstd};\nuse mbtiles::Mbtiles;\nuse mlt_core::geo_types::{Coord, Geometry, Polygon};\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::mvt::mvt_to_feature_collection;\nuse rstar::{AABB, PointDistance, RTree, RTreeObject};\n\nuse super::group_by_layer;\nuse super::state::LayerGroup;\n\ntype DecodedTile = (FeatureCollection, u32, Vec<LayerGroup>, RTree<MbtGeoEntry>);\ntype BestHover = Option<(f64, (u8, u32, u32), usize, usize)>;\n\n// ---------------------------------------------------------------------------\n// Tile loading channel types\n// ---------------------------------------------------------------------------\n\npub(crate) enum TileLoadRequest {\n    Load { z: u8, x: u32, y: u32 },\n}\n\npub(crate) struct TileLoadResult {\n    pub z: u8,\n    pub x: u32,\n    pub y: u32,\n    pub data: Result<Option<Vec<u8>>, String>,\n}\n\n// ---------------------------------------------------------------------------\n// Geometry index entry (world coordinates)\n// ---------------------------------------------------------------------------\n\n/// Feature entry stored in the per-tile R-tree, using world coordinates.\n/// World coordinate space: `x ∈ [0, 1]` west→east, `y ∈ [0, 1]` north→south.\npub(crate) struct MbtGeoEntry {\n    pub layer: usize,\n    pub feat: usize,\n    pub vertices: Vec<[f64; 2]>,\n}\n\nimpl RTreeObject for MbtGeoEntry {\n    type Envelope = AABB<[f64; 2]>;\n\n    fn envelope(&self) -> Self::Envelope {\n        if self.vertices.is_empty() {\n            return AABB::from_point([0.0, 0.0]);\n        }\n        let (ax, ay, bx, by) = self.vertices.iter().fold(\n            (\n                f64::INFINITY,\n                f64::INFINITY,\n                f64::NEG_INFINITY,\n                f64::NEG_INFINITY,\n            ),\n            |(ax, ay, bx, by), v| (ax.min(v[0]), ay.min(v[1]), bx.max(v[0]), by.max(v[1])),\n        );\n        AABB::from_corners([ax, ay], [bx, by])\n    }\n}\n\nimpl PointDistance for MbtGeoEntry {\n    fn distance_2(&self, point: &[f64; 2]) -> f64 {\n        self.vertices\n            .iter()\n            .map(|v| {\n                let dx = v[0] - point[0];\n                let dy = v[1] - point[1];\n                dx * dx + dy * dy\n            })\n            .fold(f64::INFINITY, f64::min)\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Tile coordinate transform (tile-local → world)\n// ---------------------------------------------------------------------------\n\n/// Transforms coordinates from tile-local space ([0, extent]) to world space ([0, 1]).\npub(crate) struct TileTransform {\n    n: f64,   // 2^z\n    tx: f64,  // tile x index\n    ty: f64,  // tile y index\n    ext: f64, // tile extent\n}\n\nimpl TileTransform {\n    pub(crate) fn new(z: u8, tile_x: u32, tile_y: u32, extent: u32) -> Self {\n        Self {\n            n: f64::from(1u32 << z),\n            tx: f64::from(tile_x),\n            ty: f64::from(tile_y),\n            ext: f64::from(extent),\n        }\n    }\n\n    /// Convert a tile-local coordinate to world coordinates.\n    #[inline]\n    pub(crate) fn to_world(&self, c: Coord<i32>) -> [f64; 2] {\n        [\n            (self.tx + f64::from(c.x) / self.ext) / self.n,\n            (self.ty + f64::from(c.y) / self.ext) / self.n,\n        ]\n    }\n\n    /// Collect world-coordinate vertices from a polygon.\n    pub(crate) fn poly_verts(&self, poly: &Polygon<i32>) -> Vec<[f64; 2]> {\n        poly.exterior()\n            .0\n            .iter()\n            .copied()\n            .chain(poly.interiors().iter().flat_map(|r| r.0.iter().copied()))\n            .map(|c| self.to_world(c))\n            .collect()\n    }\n\n    /// Collect world-coordinate vertices from any geometry.\n    pub(crate) fn geom_verts(&self, geom: &Geometry<i32>) -> Vec<[f64; 2]> {\n        match geom {\n            Geometry::<i32>::Point(p) => vec![self.to_world(p.0)],\n            Geometry::<i32>::LineString(ls) => {\n                ls.0.iter().copied().map(|c| self.to_world(c)).collect()\n            }\n            Geometry::<i32>::MultiPoint(mp) => mp.iter().map(|p| self.to_world(p.0)).collect(),\n            Geometry::<i32>::Polygon(poly) => self.poly_verts(poly),\n            Geometry::<i32>::MultiLineString(mls) => mls\n                .iter()\n                .flat_map(|ls| ls.0.iter().copied().map(|c| self.to_world(c)))\n                .collect(),\n            Geometry::<i32>::MultiPolygon(mpoly) => {\n                mpoly.iter().flat_map(|p| self.poly_verts(p)).collect()\n            }\n            _ => vec![],\n        }\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Tile cache entry\n// ---------------------------------------------------------------------------\n\npub(crate) enum MbtTileData {\n    Loading,\n    Empty,\n    #[allow(dead_code)]\n    Error(String),\n    Loaded {\n        fc: FeatureCollection,\n        extent: u32,\n        layer_groups: Vec<LayerGroup>,\n        geo_index: RTree<MbtGeoEntry>,\n    },\n}\n\n// ---------------------------------------------------------------------------\n// Hover state\n// ---------------------------------------------------------------------------\n\n#[derive(Clone, PartialEq, Eq)]\npub(crate) struct MbtHoveredInfo {\n    pub tile: (u8, u32, u32),\n    pub layer_idx: usize,\n    pub feat_idx: usize,\n}\n\n// ---------------------------------------------------------------------------\n// MbtilesState\n// ---------------------------------------------------------------------------\n\npub(crate) struct MbtilesState {\n    #[allow(dead_code)]\n    pub path: PathBuf,\n    /// Viewport bounds in world coords: `x ∈ [0, 1]` west→east, `y ∈ [0, 1]` north→south.\n    pub vp_x0: f64,\n    pub vp_x1: f64,\n    pub vp_y0: f64,\n    pub vp_y1: f64,\n    /// Cached tile data keyed by (z, x, y) in XYZ scheme.\n    pub tiles: HashMap<(u8, u32, u32), MbtTileData>,\n    /// Currently hovered feature.\n    pub hovered: Option<MbtHoveredInfo>,\n    /// Nominal zoom level (0 = full world width); changes by ±0.5 per scroll step.\n    /// Kept in sync with viewport width after pan (`sync_zoom_f_from_vp`).\n    pub zoom_f: f64,\n    /// Last mouse cell during left-button map drag (`Drag` deltas are applied from here).\n    pub map_drag_last: Option<(u16, u16)>,\n    loading: HashSet<(u8, u32, u32)>,\n    request_tx: mpsc::SyncSender<TileLoadRequest>,\n    pub result_rx: mpsc::Receiver<TileLoadResult>,\n    /// Until the loader thread reports success, holds the one-shot init handshake receiver.\n    loader_init_rx: Option<mpsc::Receiver<Result<(), String>>>,\n    /// Fatal error from `MBTiles` open / connection (surfaced to the UI once via `take_loader_fatal`).\n    loader_fatal: Option<String>,\n}\n\nimpl MbtilesState {\n    pub(crate) fn new(path: PathBuf) -> Self {\n        let (req_tx, req_rx) = mpsc::sync_channel::<TileLoadRequest>(200);\n        let (res_tx, res_rx) = mpsc::sync_channel::<TileLoadResult>(200);\n        let (init_tx, init_rx) = mpsc::sync_channel::<Result<(), String>>(1);\n\n        let path_clone = path.clone();\n        thread::spawn(move || {\n            let rt = tokio::runtime::Builder::new_current_thread()\n                .enable_io()\n                .enable_time()\n                .build()\n                .expect(\"tokio runtime\");\n            rt.block_on(async move {\n                let mbt = match Mbtiles::new(&path_clone) {\n                    Ok(m) => m,\n                    Err(e) => {\n                        let _ = init_tx.send(Err(format!(\"Failed to open mbtiles: {e}\")));\n                        return;\n                    }\n                };\n                let mut conn = match mbt.open_readonly().await {\n                    Ok(c) => c,\n                    Err(e) => {\n                        let _ =\n                            init_tx.send(Err(format!(\"MBTiles read-only connection failed: {e}\")));\n                        return;\n                    }\n                };\n                if init_tx.send(Ok(())).is_err() {\n                    return;\n                }\n                drop(init_tx);\n                while let Ok(TileLoadRequest::Load { z, x, y }) = req_rx.recv() {\n                    let result = mbt\n                        .get_tile(&mut conn, z, x, y)\n                        .await\n                        .map_err(|e| e.to_string());\n                    let _ = res_tx.send(TileLoadResult {\n                        z,\n                        x,\n                        y,\n                        data: result,\n                    });\n                }\n            });\n        });\n\n        Self {\n            path,\n            vp_x0: 0.0,\n            vp_x1: 1.0,\n            vp_y0: 0.0,\n            vp_y1: 1.0,\n            tiles: HashMap::new(),\n            hovered: None,\n            zoom_f: 0.0,\n            map_drag_last: None,\n            loading: HashSet::new(),\n            request_tx: req_tx,\n            result_rx: res_rx,\n            loader_init_rx: Some(init_rx),\n            loader_fatal: None,\n        }\n    }\n\n    pub(crate) fn take_loader_fatal(&mut self) -> Option<String> {\n        self.loader_fatal.take()\n    }\n\n    /// Tile zoom used for loading tiles: floor of `-log2(viewport width)`.\n    pub(crate) fn zoom_level(&self) -> u8 {\n        let vp_w = self.vp_x1 - self.vp_x0;\n        if vp_w <= 0.0 {\n            return 0;\n        }\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let z = (-vp_w.log2()).floor().clamp(0.0, 22.0) as u8;\n        z\n    }\n\n    /// `(z, 2^z as f64, 2^z)` for the current view tile grid.\n    fn view_tile_scale(&self) -> (u8, f64, u32) {\n        let z = self.zoom_level();\n        let n = 1u32 << z;\n        (z, f64::from(n), n)\n    }\n\n    /// XYZ tile indices at zoom `z` containing world point `(wx, wy)` (XYZ, clamped).\n    pub(crate) fn world_to_tile_xy(z: u8, wx: f64, wy: f64) -> (u32, u32) {\n        let n = 1u32 << z;\n        let nf = f64::from(n);\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let tx = (wx * nf).max(0.0).floor() as u32;\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let ty = (wy * nf).max(0.0).floor() as u32;\n        let tx = tx.min(n.saturating_sub(1));\n        let ty = ty.min(n.saturating_sub(1));\n        (tx, ty)\n    }\n\n    /// Map-area fractions `rx, ry` (0 = left/top of map widget) to world coordinates.\n    pub(crate) fn viewport_world_at_fracs(&self, rx: f64, ry: f64) -> (f64, f64) {\n        let wx = self.vp_x0 + rx * (self.vp_x1 - self.vp_x0);\n        let wy = self.vp_y0 + ry * (self.vp_y1 - self.vp_y0);\n        (wx, wy)\n    }\n\n    fn sync_zoom_f_from_vp(&mut self) {\n        let vp_w = self.vp_x1 - self.vp_x0;\n        if vp_w > 0.0 {\n            self.zoom_f = (-vp_w.log2()).clamp(0.0, 22.0);\n        }\n    }\n\n    /// XYZ tile indices visible in the current viewport at the current zoom.\n    pub(crate) fn visible_tiles(&self) -> Vec<(u8, u32, u32)> {\n        let (z, nf, n) = self.view_tile_scale();\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let x_min = (self.vp_x0 * nf).max(0.0).floor() as u32;\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let x_max = ((self.vp_x1 * nf).ceil() as u32)\n            .saturating_sub(1)\n            .min(n.saturating_sub(1));\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let y_min = (self.vp_y0 * nf).max(0.0).floor() as u32;\n        #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]\n        let y_max = ((self.vp_y1 * nf).ceil() as u32)\n            .saturating_sub(1)\n            .min(n.saturating_sub(1));\n        let mut tiles = Vec::new();\n        for ty in y_min..=y_max {\n            for tx in x_min..=x_max {\n                tiles.push((z, tx, ty));\n            }\n        }\n        tiles\n    }\n\n    /// XYZ tile (at current tile zoom) that contains the viewport center.\n    pub(crate) fn center_tile_xyz(&self) -> (u8, u32, u32) {\n        let z = self.zoom_level();\n        let wx = f64::midpoint(self.vp_x0, self.vp_x1);\n        let wy = f64::midpoint(self.vp_y0, self.vp_y1);\n        let (tx, ty) = Self::world_to_tile_xy(z, wx, wy);\n        (z, tx, ty)\n    }\n\n    /// Fit the viewport exactly to XYZ tile `(z, tx, ty)` and sync `zoom_f`.\n    pub(crate) fn set_viewport_to_tile(&mut self, z: u8, tx: u32, ty: u32) -> Result<(), String> {\n        let n = 1u32\n            .checked_shl(u32::from(z))\n            .ok_or_else(|| format!(\"zoom z={z} is too large\"))?;\n        if tx >= n || ty >= n {\n            return Err(format!(\n                \"tile index out of range for z={z}: x={tx} y={ty} (max {})\",\n                n.saturating_sub(1)\n            ));\n        }\n        let nf = f64::from(n);\n        self.vp_x0 = f64::from(tx) / nf;\n        self.vp_x1 = f64::from(tx + 1) / nf;\n        self.vp_y0 = f64::from(ty) / nf;\n        self.vp_y1 = f64::from(ty + 1) / nf;\n        self.sync_zoom_f_from_vp();\n        Ok(())\n    }\n\n    /// Enqueue a tile for loading if not already cached or loading.\n    pub(crate) fn request_tile(&mut self, z: u8, x: u32, y: u32) {\n        let key = (z, x, y);\n        if !self.tiles.contains_key(&key) && !self.loading.contains(&key) {\n            self.loading.insert(key);\n            self.tiles.insert(key, MbtTileData::Loading);\n            if self\n                .request_tx\n                .try_send(TileLoadRequest::Load { z, x, y })\n                .is_err()\n            {\n                self.loading.remove(&key);\n                self.tiles.insert(\n                    key,\n                    MbtTileData::Error(\n                        \"Could not enqueue tile load (channel full or loader stopped)\".into(),\n                    ),\n                );\n            }\n        }\n    }\n\n    /// Request this tile and every XYZ ancestor down to z=0 (for overzoom fallbacks).\n    pub(crate) fn request_tile_with_ancestors(&mut self, z: u8, x: u32, y: u32) {\n        let mut cz = z;\n        let mut cx = x;\n        let mut cy = y;\n        loop {\n            self.request_tile(cz, cx, cy);\n            if cz == 0 {\n                break;\n            }\n            cz -= 1;\n            cx >>= 1;\n            cy >>= 1;\n        }\n    }\n\n    /// First loaded ancestor for overzoom (`k` levels up: tile `(x>>k, y>>k)` at `z-k`), if any.\n    pub(crate) fn find_overzoom_source(&self, z: u8, tx: u32, ty: u32) -> Option<(u8, u32, u32)> {\n        for k in 1..=z {\n            let pz = z - k;\n            let shift = u32::from(k);\n            let px = tx >> shift;\n            let py = ty >> shift;\n            let key = (pz, px, py);\n            if matches!(self.tiles.get(&key), Some(MbtTileData::Loaded { .. })) {\n                return Some(key);\n            }\n        }\n        None\n    }\n\n    /// Tile to use for data at `(z,tx,ty)`: native if loaded, otherwise first loaded ancestor.\n    pub(crate) fn effective_tile_key(&self, z: u8, tx: u32, ty: u32) -> Option<(u8, u32, u32)> {\n        let key = (z, tx, ty);\n        match self.tiles.get(&key) {\n            Some(MbtTileData::Loaded { .. }) => Some(key),\n            _ => self.find_overzoom_source(z, tx, ty),\n        }\n    }\n\n    /// Drain incoming results and update the tile cache. Returns true if any tiles changed.\n    pub(crate) fn process_results(&mut self) -> bool {\n        let mut changed = false;\n\n        if let Some(rx) = self.loader_init_rx.take() {\n            match rx.try_recv() {\n                Ok(Ok(())) => {\n                    // Init succeeded; drop the receiver so we do not treat later disconnect as init failure.\n                }\n                Ok(Err(msg)) => {\n                    self.loader_fatal = Some(msg);\n                    changed = true;\n                }\n                Err(TryRecvError::Empty) => {\n                    self.loader_init_rx = Some(rx);\n                }\n                Err(TryRecvError::Disconnected) => {\n                    if self.loader_fatal.is_none() {\n                        self.loader_fatal =\n                            Some(\"MBTiles loader thread exited during initialization\".into());\n                    }\n                    changed = true;\n                }\n            }\n        }\n\n        while let Ok(res) = self.result_rx.try_recv() {\n            let key = (res.z, res.x, res.y);\n            self.loading.remove(&key);\n            let entry = match res.data {\n                Err(e) => MbtTileData::Error(e),\n                Ok(None) => MbtTileData::Empty,\n                Ok(Some(raw)) => match decode_and_parse(res.z, res.x, res.y, raw) {\n                    Ok(Some((fc, extent, layer_groups, geo_index))) => MbtTileData::Loaded {\n                        fc,\n                        extent,\n                        layer_groups,\n                        geo_index,\n                    },\n                    Ok(None) => MbtTileData::Empty,\n                    Err(e) => MbtTileData::Error(e.to_string()),\n                },\n            };\n            self.tiles.insert(key, entry);\n            changed = true;\n        }\n        changed\n    }\n\n    /// Zoom in/out by half a zoom level around `(wx, wy)` (scroll wheel).\n    pub(crate) fn zoom_wheel_at(&mut self, wx: f64, wy: f64, zoom_in: bool) {\n        let dz = if zoom_in { 0.5 } else { -0.5 };\n        let new_z = (self.zoom_f + dz).clamp(0.0, 22.0);\n        if (new_z - self.zoom_f).abs() < 1e-12 {\n            return;\n        }\n        let scale = 2_f64.powf(-(new_z - self.zoom_f));\n        if !self.zoom_viewport_at(wx, wy, scale) {\n            return;\n        }\n        self.sync_zoom_f_from_vp();\n    }\n\n    /// Scale the viewport around `(wx, wy)` by `scale` on both axes (`scale` \\< 1 zooms in).\n    fn zoom_viewport_at(&mut self, wx: f64, wy: f64, scale: f64) -> bool {\n        let x0 = wx + (self.vp_x0 - wx) * scale;\n        let x1 = wx + (self.vp_x1 - wx) * scale;\n        let y0 = wy + (self.vp_y0 - wy) * scale;\n        let y1 = wy + (self.vp_y1 - wy) * scale;\n        let min_size = 2_f64.powf(-22.0);\n        if x1 - x0 < min_size || y1 - y0 < min_size {\n            return false;\n        }\n        self.vp_x0 = x0;\n        self.vp_x1 = x1;\n        self.vp_y0 = y0;\n        self.vp_y1 = y1;\n        true\n    }\n\n    /// Pan the viewport by mouse delta in terminal cells (`d_col`/`d_row` = current − previous).\n    pub(crate) fn pan_by_pixels(&mut self, area_w: u16, area_h: u16, d_col: i32, d_row: i32) {\n        if area_w == 0 || area_h == 0 {\n            return;\n        }\n        let wf = f64::from(area_w);\n        let hf = f64::from(area_h);\n        let vp_w = self.vp_x1 - self.vp_x0;\n        let vp_h = self.vp_y1 - self.vp_y0;\n        let dx = f64::from(d_col) / wf * vp_w;\n        let dy = f64::from(d_row) / hf * vp_h;\n        self.vp_x0 -= dx;\n        self.vp_x1 -= dx;\n        self.vp_y0 -= dy;\n        self.vp_y1 -= dy;\n        self.sync_zoom_f_from_vp();\n    }\n\n    /// Find the nearest feature to world point (wx, wy) among visible cells (native or overzoom).\n    pub(crate) fn find_hovered(&mut self, wx: f64, wy: f64) {\n        let z = self.zoom_level();\n        let threshold = (self.vp_x1 - self.vp_x0) * 0.02;\n        let thresh_sq = threshold * threshold;\n        let pt = [wx, wy];\n\n        let mut best: BestHover = None;\n        let mut seen_src: HashSet<(u8, u32, u32)> = HashSet::new();\n        let visible = self.visible_tiles();\n        for (tz, tx, ty) in visible {\n            if tz != z {\n                continue;\n            }\n            let Some(src_key) = self.effective_tile_key(tz, tx, ty) else {\n                continue;\n            };\n            if !seen_src.insert(src_key) {\n                continue;\n            }\n            let Some(MbtTileData::Loaded { geo_index, .. }) = self.tiles.get(&src_key) else {\n                continue;\n            };\n            for e in geo_index.nearest_neighbor_iter(&pt) {\n                let d = e.distance_2(&pt);\n                if d > thresh_sq {\n                    break;\n                }\n                if best.is_none_or(|(bd, ..)| d < bd) {\n                    best = Some((d, src_key, e.layer, e.feat));\n                }\n            }\n        }\n        let new_hov = best.map(|(_, tile, layer, feat)| MbtHoveredInfo {\n            tile,\n            layer_idx: layer,\n            feat_idx: feat,\n        });\n        self.hovered = new_hov;\n    }\n\n    /// Keys we keep when pruning: visible tiles (with a 1-tile margin) plus every ancestor chain.\n    fn keep_tile_keys(&self) -> HashSet<(u8, u32, u32)> {\n        let z = self.zoom_level();\n        let Some(n) = 1u32.checked_shl(u32::from(z)) else {\n            return HashSet::new();\n        };\n        let mut out = HashSet::new();\n        for (_zv, tx, ty) in self.visible_tiles() {\n            for dx in -1..=1 {\n                for dy in -1..=1 {\n                    #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]\n                    let nx = (i64::from(tx) + i64::from(dx))\n                        .clamp(0, i64::from(n.saturating_sub(1)))\n                        as u32;\n                    #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]\n                    let ny = (i64::from(ty) + i64::from(dy))\n                        .clamp(0, i64::from(n.saturating_sub(1)))\n                        as u32;\n                    let mut cz = z;\n                    let mut cx = nx;\n                    let mut cy = ny;\n                    loop {\n                        out.insert((cz, cx, cy));\n                        if cz == 0 {\n                            break;\n                        }\n                        cz -= 1;\n                        cx >>= 1;\n                        cy >>= 1;\n                    }\n                }\n            }\n        }\n        out\n    }\n\n    /// Drop cached tiles far from the viewport so panning does not grow memory without bound.\n    pub(crate) fn prune_tile_cache_if_needed(&mut self) {\n        const MAX: usize = 256;\n        if self.tiles.len() <= MAX {\n            return;\n        }\n        let keep = self.keep_tile_keys();\n        let stale: Vec<(u8, u32, u32)> = self\n            .tiles\n            .keys()\n            .filter(|k| !keep.contains(k))\n            .copied()\n            .collect();\n        let stale_set: HashSet<_> = stale.iter().copied().collect();\n        if self\n            .hovered\n            .as_ref()\n            .is_some_and(|h| stale_set.contains(&h.tile))\n        {\n            self.hovered = None;\n        }\n        for k in stale {\n            self.loading.remove(&k);\n            self.tiles.remove(&k);\n        }\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Tile decode & parse helpers\n// ---------------------------------------------------------------------------\n\nfn decode_and_parse(z: u8, tx: u32, ty: u32, raw: Vec<u8>) -> anyhow::Result<Option<DecodedTile>> {\n    let buf = decompress(raw)?;\n    if buf.is_empty() {\n        return Ok(None);\n    }\n    let fc = mvt_to_feature_collection(buf)?;\n    if fc.features.is_empty() {\n        return Ok(None);\n    }\n    let extent = fc\n        .features\n        .first()\n        .and_then(|f| f.properties.get(\"_extent\"))\n        .and_then(serde_json::Value::as_u64)\n        .map_or(4096, |v| u32::try_from(v).unwrap_or(4096));\n    let layer_groups = group_by_layer(&fc);\n    let geo_index = build_world_geo_index(z, tx, ty, &fc, &layer_groups, extent);\n    Ok(Some((fc, extent, layer_groups, geo_index)))\n}\n\nfn decompress(raw: Vec<u8>) -> anyhow::Result<Vec<u8>> {\n    if raw.len() >= 2 && raw[0] == 0x1f && raw[1] == 0x8b {\n        Ok(decode_gzip(&raw)?)\n    } else if raw.len() >= 4 && raw[0] == 0x28 && raw[1] == 0xb5 && raw[2] == 0x2f && raw[3] == 0xfd\n    {\n        Ok(decode_zstd(&raw)?)\n    } else {\n        Ok(raw)\n    }\n}\n\nfn build_world_geo_index(\n    z: u8,\n    tx: u32,\n    ty: u32,\n    fc: &FeatureCollection,\n    layer_groups: &[LayerGroup],\n    extent: u32,\n) -> RTree<MbtGeoEntry> {\n    let transform = TileTransform::new(z, tx, ty, extent);\n    let mut entries = Vec::new();\n    for (li, group) in layer_groups.iter().enumerate() {\n        for (fi, &gi) in group.feature_indices.iter().enumerate() {\n            let geom = &fc.features[gi].geometry;\n            let vertices = transform.geom_verts(geom);\n            if !vertices.is_empty() {\n                entries.push(MbtGeoEntry {\n                    layer: li,\n                    feat: fi,\n                    vertices,\n                });\n            }\n        }\n    }\n    RTree::bulk_load(entries)\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/mod.rs",
    "content": "//! TUI visualizer for MLT files using ratatui\n\npub(crate) mod mbt;\nmod rendering;\nmod state;\n\nuse std::collections::HashSet;\nuse std::fs::canonicalize;\nuse std::io::Write as _;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc;\nuse std::time::{Duration, Instant};\nuse std::{fs, thread};\n\nuse anyhow::bail;\nuse clap::Args;\nuse crossterm::event::{\n    self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, KeyModifiers,\n    MouseButton, MouseEventKind,\n};\nuse crossterm::execute;\nuse mlt_core::geo_types::{Coord, Geometry, Polygon};\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::mvt::mvt_to_feature_collection;\nuse mlt_core::{Decoder, GeometryType, Parser};\nuse ratatui::layout::{Constraint, Direction, Layout, Margin, Rect};\nuse ratatui::style::{Color, Modifier, Style};\nuse ratatui::text::{Line, Span};\nuse ratatui::widgets::{Block, Borders};\nuse rstar::{AABB, PointDistance, RTreeObject};\n\nuse crate::ls::{\n    FileAlgorithm, FileSortColumn, LsFlags, LsRow, analyze_tile_files, is_mbt_extension,\n    is_mlt_extension, is_tile_extension,\n};\nuse crate::ui::mbt::MbtilesState;\nuse crate::ui::rendering::files::{\n    render_file_browser, render_file_filter_panel, render_file_info_panel,\n    render_tile_preview_panel,\n};\nuse crate::ui::rendering::help::{render_error_popup, render_help_overlay};\nuse crate::ui::rendering::layers::{\n    render_mbtiles_hover_panel, render_properties_panel, render_tree_panel,\n};\nuse crate::ui::rendering::map::{render_map_panel, render_mbtiles_map_panel};\nuse crate::ui::state::{App, HoveredInfo, LayerGroup, ResizeHandle, TreeItem, ViewMode};\n\npub const CLR_POINT: Color = Color::Magenta;\npub const CLR_MULTI_POINT: Color = Color::LightMagenta;\npub const CLR_LINE: Color = Color::Cyan;\npub const CLR_MULTI_LINE: Color = Color::LightCyan;\npub const CLR_POLYGON: Color = Color::Blue;\npub const CLR_MULTI_POLYGON: Color = Color::LightBlue;\npub const CLR_INNER_RING: Color = Color::Red;\npub const CLR_BAD_WINDING: Color = Color::LightRed;\npub const CLR_EXTENT: Color = Color::DarkGray;\npub const CLR_SELECTED: Color = Color::Yellow;\npub const CLR_HOVERED: Color = Color::White;\npub const CLR_HOVERED_TREE: Color = Color::LightGreen;\npub const CLR_DIMMED: Color = Color::DarkGray;\npub const CLR_INNER_RING_SEL: Color = Color::Rgb(255, 150, 120);\npub const CLR_LABEL: Color = Color::Cyan;\npub const CLR_HINT: Color = Color::DarkGray;\n\npub const STYLE_SELECTED: Style = Style::new().fg(CLR_SELECTED).add_modifier(Modifier::BOLD);\npub const STYLE_LABEL: Style = Style::new().fg(CLR_LABEL);\npub const STYLE_BOLD: Style = Style::new().add_modifier(Modifier::BOLD);\n\n/// Throttle hover-driven redraws so mouse move over map doesn't flood the loop\nconst HOVER_REDRAW_THROTTLE: Duration = Duration::from_millis(32);\n\n#[derive(Args)]\npub struct UiArgs {\n    /// Path to a tile file (`.mlt`, `.mvt`, `.pbf`, `.mbtiles`) or directory\n    path: PathBuf,\n    /// Start `MBTiles` map centered on this XYZ tile (`z/x/y`, e.g. `6/32/21`). `MBTiles` only.\n    #[arg(long = \"center-tile\", value_name = \"Z/X/Y\")]\n    center_tile: Option<String>,\n}\n\npub fn ui(args: &UiArgs) -> anyhow::Result<()> {\n    if args.center_tile.is_some() && !is_mbt_extension(&args.path) {\n        bail!(\"--center-tile is only supported when opening an .mbtiles file\");\n    }\n    let app = if is_mbt_extension(&args.path) {\n        let mut mbt = MbtilesState::new(args.path.clone());\n        if let Some(ref s) = args.center_tile {\n            let (z, x, y) = parse_center_tile_xyz(s)?;\n            mbt.set_viewport_to_tile(z, x, y)\n                .map_err(|e| anyhow::anyhow!(e))?;\n        }\n        App::new_mbtiles(mbt, args.path.clone())\n    } else if args.path.is_dir() {\n        let paths = find_tile_files(&args.path)?;\n        if paths.is_empty() {\n            bail!(\n                \"No tile files found in {}\",\n                canonicalize(&args.path)?.display()\n            );\n        }\n        let base = args.path.clone();\n        let files: Vec<LsRow> = paths\n            .iter()\n            .map(|p| LsRow::Loading { path: p.clone() })\n            .collect();\n        let (tx, rx) = mpsc::channel();\n        thread::spawn(move || {\n            let _ = tx.send(analyze_tile_files(\n                &paths,\n                &base,\n                LsFlags {\n                    gzip: true,\n                    algorithms: true,\n                    validate: false,\n                },\n            ));\n        });\n        App::new_file_browser(files, Some(rx), args.path.clone())\n    } else if args.path.is_file() {\n        App::new_single_file(load_fc(&args.path)?, Some(args.path.clone()))\n    } else {\n        bail!(\"Path is not a file or directory\");\n    };\n    run_app(app)\n}\n\nfn parse_center_tile_xyz(spec: &str) -> anyhow::Result<(u8, u32, u32)> {\n    let parts: Vec<&str> = spec\n        .split('/')\n        .map(str::trim)\n        .filter(|p| !p.is_empty())\n        .collect();\n    if parts.len() != 3 {\n        bail!(\"--center-tile must be z/x/y (three integers), got {spec:?}\");\n    }\n    let zoom: u8 = parts[0]\n        .parse()\n        .map_err(|_| anyhow::anyhow!(\"invalid zoom z in --center-tile: {:?}\", parts[0]))?;\n    let tile_x: u32 = parts[1]\n        .parse()\n        .map_err(|_| anyhow::anyhow!(\"invalid tile x in --center-tile: {:?}\", parts[1]))?;\n    let tile_y: u32 = parts[2]\n        .parse()\n        .map_err(|_| anyhow::anyhow!(\"invalid tile y in --center-tile: {:?}\", parts[2]))?;\n    let grid_n = 1u32\n        .checked_shl(u32::from(zoom))\n        .ok_or_else(|| anyhow::anyhow!(\"zoom z={zoom} is too large\"))?;\n    if tile_x >= grid_n || tile_y >= grid_n {\n        bail!(\n            \"tile x/y must be < 2^z for z={zoom} (max index {})\",\n            grid_n.saturating_sub(1)\n        );\n    }\n    Ok((zoom, tile_x, tile_y))\n}\n\n// --- Data loading ---\n\nfn load_fc(path: &Path) -> anyhow::Result<FeatureCollection> {\n    let buf = fs::read(path)?;\n    if is_mlt_extension(path) {\n        let layers = Decoder::default().decode_all(Parser::default().parse_layers(&buf)?)?;\n        Ok(FeatureCollection::from_layers(layers)?)\n    } else {\n        Ok(mvt_to_feature_collection(buf)?)\n    }\n}\n\nfn extent_from_fc(fc: &FeatureCollection) -> u32 {\n    fc.features\n        .first()\n        .and_then(|f| {\n            f.properties\n                .get(\"_extent\")\n                .and_then(serde_json::Value::as_u64)\n        })\n        .map_or(4096, |v| u32::try_from(v).expect(\"_extent is valid u32\"))\n}\n\nfn refresh_tile_preview(app: &mut App) {\n    let path = if let Some(r) = app.get_selected_file() {\n        r.path().to_path_buf()\n    } else {\n        app.preview_tile_path = None;\n        app.preview_fc = None;\n        app.preview_load_requested = None;\n        return;\n    };\n    if !is_tile_extension(&path) {\n        app.preview_tile_path = None;\n        app.preview_fc = None;\n        app.preview_load_requested = None;\n    }\n}\n\nfn group_by_layer(fc: &FeatureCollection) -> Vec<LayerGroup> {\n    let mut groups: Vec<LayerGroup> = Vec::new();\n    for (i, f) in fc.features.iter().enumerate() {\n        let name = f\n            .properties\n            .get(\"_layer\")\n            .and_then(|v| v.as_str())\n            .unwrap_or(\"unknown\");\n        let extent = f\n            .properties\n            .get(\"_extent\")\n            .and_then(serde_json::Value::as_u64)\n            .map_or(4096, |v| u32::try_from(v).expect(\"_extent is valid u32\"));\n        if let Some(g) = groups.iter_mut().find(|g| g.name == name) {\n            g.feature_indices.push(i);\n        } else {\n            groups.push(LayerGroup::new(name.to_string(), extent, vec![i]));\n        }\n    }\n    groups\n}\n\nfn auto_expand(groups: &[LayerGroup]) -> Vec<bool> {\n    if groups.len() == 1 {\n        vec![true]\n    } else {\n        vec![false; groups.len()]\n    }\n}\n\nfn find_tile_files(dir: &Path) -> anyhow::Result<Vec<PathBuf>> {\n    fn visit(dir: &Path, out: &mut Vec<PathBuf>) -> anyhow::Result<()> {\n        if dir.is_dir() {\n            for entry in fs::read_dir(dir)? {\n                let p = entry?.path();\n                if p.is_dir() {\n                    visit(&p, out)?;\n                } else if is_tile_extension(&p) {\n                    out.push(p);\n                }\n            }\n        }\n        Ok(())\n    }\n    let mut files = Vec::new();\n    visit(dir, &mut files)?;\n    files.sort_unstable();\n    Ok(files)\n}\n\n// --- Hit testing ---\n\nfn point_in_rect(col: u16, row: u16, r: Rect) -> bool {\n    col >= r.x && col < r.x + r.width && row >= r.y && row < r.y + r.height\n}\n\nfn click_row_in_area(col: u16, row: u16, area: Rect, scroll: usize) -> Option<usize> {\n    let top = area.y + 1;\n    let bot = area.y + area.height.saturating_sub(1);\n    (col >= area.x && col < area.x + area.width && row >= top && row < bot)\n        .then(|| (row - top) as usize + scroll)\n}\n\nconst HIGHLIGHT_SYMBOL_WIDTH: u16 = 3;\nconst COLUMN_SPACING: u16 = 1;\n\n/// Table has 6 columns: File, Size, Enc %, Layers, Features, Notes. Notes is not sortable.\nfn file_header_click_column(\n    area: Rect,\n    widths: &[Constraint; 6],\n    col: u16,\n    row: u16,\n) -> Option<FileSortColumn> {\n    if row != area.y + 1 {\n        return None;\n    }\n    let inner = area.inner(Margin {\n        vertical: 1,\n        horizontal: 1,\n    });\n    let fixed: u16 = widths\n        .iter()\n        .map(|c| match c {\n            Constraint::Length(l) => *l,\n            _ => 0,\n        })\n        .sum();\n    let remaining = inner\n        .width\n        .saturating_sub(HIGHLIGHT_SYMBOL_WIDTH + fixed + 5 * COLUMN_SPACING);\n    let mut resolved = [0u16; 6];\n    for (i, c) in widths.iter().enumerate() {\n        resolved[i] = match c {\n            Constraint::Length(l) => *l,\n            Constraint::Min(_) => remaining,\n            _ => 0,\n        };\n    }\n    let cols = [\n        FileSortColumn::File,\n        FileSortColumn::Size,\n        FileSortColumn::EncPct,\n        FileSortColumn::Layers,\n        FileSortColumn::Features,\n    ];\n    let mut x = inner.x + HIGHLIGHT_SYMBOL_WIDTH;\n    for (i, &w) in resolved.iter().enumerate() {\n        let end = if i == resolved.len() - 1 {\n            inner.x + inner.width\n        } else {\n            x + w\n        };\n        if col >= x && col < end {\n            return cols.get(i).copied();\n        }\n        x = end + COLUMN_SPACING;\n    }\n    None\n}\n\nfn block_with_title(title: impl Into<Line<'static>>) -> Block<'static> {\n    Block::default().borders(Borders::ALL).title(title)\n}\n\n/// Area where the map canvas actually draws (inside borders + title).\n/// Must stay in sync with `render_map_panel` and `render_mbtiles_map_panel`, which both use\n/// `block_with_title`.\nfn map_canvas_area(outer: Rect) -> Rect {\n    block_with_title(\" \").inner(outer)\n}\n\n/// Helper to build `Span::styled(format!(\"{name}: \"), STYLE_LABEL)` + raw value.\nfn stat_line(name: &str, val: &dyn std::fmt::Display) -> Line<'static> {\n    Line::from(vec![\n        Span::styled(format!(\"{name}: \"), STYLE_LABEL),\n        Span::raw(val.to_string()),\n    ])\n}\n\nconst DIVIDER_GRAB: u16 = 2;\n\nfn divider_hit(\n    col: u16,\n    row: u16,\n    left: Rect,\n    tree: Rect,\n    geom: Option<Rect>,\n) -> Option<ResizeHandle> {\n    let horiz_hit = |dy: u16| {\n        row >= dy.saturating_sub(DIVIDER_GRAB)\n            && row < dy.saturating_add(DIVIDER_GRAB)\n            && col >= left.x\n            && col < left.x + left.width\n    };\n\n    // Check horizontal dividers (most specific first)\n    if geom.is_some_and(|g| horiz_hit(g.y)) {\n        return Some(ResizeHandle::PropertiesGeometry);\n    }\n    if horiz_hit(tree.y + tree.height) {\n        return Some(ResizeHandle::FeaturesProperties);\n    }\n\n    // Check vertical divider (between left panel and map)\n    let dx = left.x + left.width;\n    if col >= dx.saturating_sub(DIVIDER_GRAB)\n        && col < dx.saturating_add(DIVIDER_GRAB)\n        && row >= left.y\n        && row < left.y + left.height\n    {\n        return Some(ResizeHandle::LeftRight);\n    }\n\n    None\n}\n\n// --- App loop ---\n\n/// OSC 22: set mouse pointer shape\nfn set_pointer_cursor(pointer: bool) {\n    let seq: &[u8] = if pointer {\n        b\"\\x1b]22;default\\x1b\\\\\"\n    } else {\n        b\"\\x1b]22;\\x1b\\\\\"\n    };\n    let _ = std::io::stdout().write_all(seq);\n    let _ = std::io::stdout().flush();\n}\n\nfn run_app(mut app: App) -> anyhow::Result<()> {\n    let mut terminal = ratatui::init();\n    set_pointer_cursor(true);\n    let result = (|| {\n        execute!(terminal.backend_mut(), EnableMouseCapture)?;\n        run_app_loop(&mut terminal, &mut app)\n    })();\n    let _ = execute!(terminal.backend_mut(), DisableMouseCapture);\n    set_pointer_cursor(false);\n    ratatui::restore();\n    result\n}\n\n/// Compute percentage position (clamped to 10..=90) for drag resizing.\nfn pct_at(pos: u16, origin: u16, span: u16) -> u16 {\n    #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]\n    let pct =\n        (f32::from(pos.saturating_sub(origin)) / f32::from(span.max(1)) * 100.0).round() as u16;\n    pct.clamp(10, 90)\n}\n\nconst FILE_RIGHT_PANEL_MIN: u16 = 20;\nfn file_browser_preview_pct_clamped(pct: u16, filter_pct: u16) -> u16 {\n    pct.clamp(\n        FILE_RIGHT_PANEL_MIN,\n        100u16\n            .saturating_sub(FILE_RIGHT_PANEL_MIN)\n            .saturating_sub(filter_pct),\n    )\n}\nfn file_browser_filter_pct_clamped(pct: u16, preview_pct: u16) -> u16 {\n    pct.clamp(\n        FILE_RIGHT_PANEL_MIN,\n        100u16\n            .saturating_sub(FILE_RIGHT_PANEL_MIN)\n            .saturating_sub(preview_pct),\n    )\n}\n\nfn run_app_loop(terminal: &mut ratatui::DefaultTerminal, app: &mut App) -> anyhow::Result<()> {\n    let mut map_area: Option<Rect> = None;\n    let mut tree_area: Option<Rect> = None;\n    let mut props_area: Option<Rect> = None;\n    let mut geom_area: Option<Rect> = None;\n    let mut left_area: Option<Rect> = None;\n    let mut filter_area: Option<Rect> = None;\n    let mut info_area: Option<Rect> = None;\n    let mut preview_area: Option<Rect> = None;\n    let mut right_col: Option<Rect> = None;\n    let mut file_left: Option<Rect> = None;\n    let mut last_tree_click: Option<(Instant, usize)> = None;\n    let mut last_file_click: Option<(Instant, usize)> = None;\n    let mut last_hover_redraw: Option<Instant> = None;\n\n    loop {\n        // Process incoming mbtiles tile results and request visible tiles.\n        if app.mode == ViewMode::MbtilesMap\n            && let Some(ref mut mbt) = app.mbt_state\n        {\n            if mbt.process_results() {\n                app.needs_redraw = true;\n            }\n            if let Some(msg) = mbt.take_loader_fatal() {\n                let title = app\n                    .current_file\n                    .as_ref()\n                    .map_or_else(|| \"MBTiles\".to_string(), |p| p.display().to_string());\n                app.error_popup = Some((title, msg));\n                app.needs_redraw = true;\n            }\n            let visible = mbt.visible_tiles();\n            for (z, x, y) in visible {\n                mbt.request_tile_with_ancestors(z, x, y);\n            }\n            mbt.prune_tile_cache_if_needed();\n        }\n\n        if let Some(rows) = app.analysis_rx.as_ref().and_then(|rx| rx.try_recv().ok()) {\n            if rows.len() == app.files.len() {\n                for (i, row) in rows.into_iter().enumerate() {\n                    if let Some(e) = app.files.get_mut(i) {\n                        *e = row;\n                    }\n                }\n            }\n            app.analysis_rx = None;\n            app.rebuild_filtered_files();\n        }\n\n        if let Some((path, result)) = app.preview_rx.as_mut().and_then(|rx| rx.try_recv().ok()) {\n            app.preview_rx = None;\n            app.preview_load_requested = None;\n            if app.get_selected_file().map(LsRow::path) == Some(path.as_path()) {\n                if let Ok((fc, ext)) = result {\n                    app.preview_tile_path = Some(path);\n                    app.preview_fc = Some(fc);\n                    app.preview_extent = ext;\n                } else {\n                    app.preview_tile_path = Some(path);\n                    app.preview_fc = None;\n                }\n            }\n            app.invalidate();\n        }\n        if app.mode == ViewMode::FileBrowser\n            && let Some(selected) = app.get_selected_file()\n        {\n            let path = selected.path().to_path_buf();\n            if is_tile_extension(&path)\n                && (app.preview_tile_path.as_ref() != Some(&path) || app.preview_fc.is_none())\n                && app.preview_load_requested.as_ref() != Some(&path)\n            {\n                let (tx, rx) = mpsc::channel();\n                app.preview_rx = Some(rx);\n                app.preview_load_requested = Some(path.clone());\n                let path_spawn = path.clone();\n                thread::spawn(move || {\n                    let result = load_fc(&path_spawn)\n                        .map(|fc| {\n                            let ext = extent_from_fc(&fc);\n                            (fc, ext)\n                        })\n                        .map_err(|_| ());\n                    let _ = tx.send((path_spawn, result));\n                });\n            }\n        }\n\n        if app.needs_redraw {\n            app.needs_redraw = false;\n            terminal.draw(|f| {\n                match app.mode {\n                    ViewMode::FileBrowser => {\n                        refresh_tile_preview(app);\n                        let cols = Layout::default()\n                            .direction(Direction::Horizontal)\n                            .constraints([\n                                Constraint::Percentage(app.file_left_pct),\n                                Constraint::Percentage(100u16.saturating_sub(app.file_left_pct)),\n                            ])\n                            .split(f.area());\n                        let right_pct = file_browser_preview_pct_clamped(\n                            app.file_preview_pct,\n                            app.file_filter_pct,\n                        );\n                        let filter_pct =\n                            file_browser_filter_pct_clamped(app.file_filter_pct, right_pct);\n                        app.file_preview_pct = right_pct;\n                        app.file_filter_pct = filter_pct;\n                        let right = Layout::default()\n                            .direction(Direction::Vertical)\n                            .constraints([\n                                Constraint::Percentage(right_pct),\n                                Constraint::Percentage(filter_pct),\n                                Constraint::Percentage(\n                                    100u16.saturating_sub(right_pct).saturating_sub(filter_pct),\n                                ),\n                            ])\n                            .split(cols[1]);\n                        render_file_browser(f, cols[0], app);\n                        render_tile_preview_panel(f, right[0], app);\n                        render_file_filter_panel(f, right[1], app);\n                        render_file_info_panel(f, right[2], app);\n                        file_left = Some(cols[0]);\n                        right_col = Some(cols[1]);\n                        preview_area = Some(right[0]);\n                        filter_area = Some(right[1]);\n                        info_area = Some(right[2]);\n                    }\n                    ViewMode::LayerOverview => {\n                        let cols = Layout::default()\n                            .direction(Direction::Horizontal)\n                            .constraints([\n                                Constraint::Percentage(app.left_pct),\n                                Constraint::Percentage(100u16.saturating_sub(app.left_pct)),\n                            ])\n                            .split(f.area());\n                        let left = Layout::default()\n                            .direction(Direction::Vertical)\n                            .constraints([\n                                Constraint::Percentage(app.features_pct),\n                                Constraint::Percentage(100u16.saturating_sub(app.features_pct)),\n                            ])\n                            .split(cols[0]);\n                        render_tree_panel(f, left[0], app);\n                        let ga = render_properties_panel(f, left[1], app);\n                        render_map_panel(f, cols[1], app);\n                        tree_area = Some(left[0]);\n                        props_area = Some(left[1]);\n                        geom_area = Some(ga);\n                        left_area = Some(cols[0]);\n                        map_area = Some(map_canvas_area(cols[1]));\n                    }\n                    ViewMode::MbtilesMap => {\n                        let cols = Layout::default()\n                            .direction(Direction::Horizontal)\n                            .constraints([\n                                Constraint::Percentage(app.left_pct),\n                                Constraint::Percentage(100u16.saturating_sub(app.left_pct)),\n                            ])\n                            .split(f.area());\n                        render_mbtiles_hover_panel(f, cols[0], app);\n                        render_mbtiles_map_panel(f, cols[1], app);\n                        left_area = Some(cols[0]);\n                        map_area = Some(map_canvas_area(cols[1]));\n                    }\n                }\n                if app.error_popup.is_some() {\n                    render_error_popup(f, app);\n                } else if app.show_help {\n                    render_help_overlay(f, app);\n                }\n            })?;\n        }\n\n        if event::poll(Duration::from_millis(16))? {\n            match event::read()? {\n                Event::Key(key) if key.kind == KeyEventKind::Press => {\n                    if key.modifiers.contains(KeyModifiers::CONTROL)\n                        && key.code == KeyCode::Char('c')\n                    {\n                        break;\n                    }\n                    if app.error_popup.is_some() {\n                        app.error_popup = None;\n                        app.invalidate();\n                        continue;\n                    }\n                    if app.show_help {\n                        match key.code {\n                            KeyCode::Up | KeyCode::Char('k') => {\n                                app.help_scroll = app.help_scroll.saturating_sub(1);\n                            }\n                            KeyCode::Down | KeyCode::Char('j') => {\n                                app.help_scroll = app.help_scroll.saturating_add(1);\n                            }\n                            KeyCode::PageUp => {\n                                app.help_scroll = app.help_scroll.saturating_sub(10);\n                            }\n                            KeyCode::PageDown => {\n                                app.help_scroll = app.help_scroll.saturating_add(10);\n                            }\n                            KeyCode::Home => app.help_scroll = 0,\n                            KeyCode::End => app.help_scroll = u16::MAX,\n                            _ => app.show_help = false,\n                        }\n                        app.invalidate();\n                        continue;\n                    }\n                    match key.code {\n                        KeyCode::Char('q') => break,\n                        KeyCode::Esc if app.handle_escape() => break,\n                        KeyCode::Char('?') | KeyCode::F(1) => app.open_help(),\n                        KeyCode::Char('h') if !key.modifiers.contains(KeyModifiers::CONTROL) => {\n                            app.open_help();\n                        }\n                        KeyCode::Enter => app.handle_enter(),\n                        KeyCode::Char('+' | '=') | KeyCode::Right => app.handle_plus(),\n                        KeyCode::Char('-') => app.handle_minus(),\n                        KeyCode::Char('*') => app.handle_star(),\n                        KeyCode::Up | KeyCode::Char('k') => app.move_up_by(1),\n                        KeyCode::Down | KeyCode::Char('j') => app.move_down_by(1),\n                        KeyCode::Left => app.handle_left_arrow(),\n                        KeyCode::PageUp => {\n                            app.move_up_by(app.page_size().saturating_sub(1).max(1));\n                        }\n                        KeyCode::PageDown => {\n                            app.move_down_by(app.page_size().saturating_sub(1).max(1));\n                        }\n                        KeyCode::Home => app.move_to_start(),\n                        KeyCode::End => app.move_to_end(),\n                        KeyCode::Char('h') if key.modifiers.contains(KeyModifiers::CONTROL) => {\n                            app.left_pct = app.left_pct.saturating_sub(5).max(10);\n                            app.invalidate();\n                        }\n                        KeyCode::Char('l') if key.modifiers.contains(KeyModifiers::CONTROL) => {\n                            app.left_pct = (app.left_pct + 5).min(90);\n                            app.invalidate();\n                        }\n                        KeyCode::Char('J') if key.modifiers.contains(KeyModifiers::SHIFT) => {\n                            app.features_pct = app.features_pct.saturating_sub(5).max(10);\n                            app.invalidate();\n                        }\n                        KeyCode::Char('K') if key.modifiers.contains(KeyModifiers::SHIFT) => {\n                            app.features_pct = (app.features_pct + 5).min(90);\n                            app.invalidate();\n                        }\n                        _ => {}\n                    }\n                }\n                Event::Mouse(mouse) if app.error_popup.is_some() => {\n                    if matches!(mouse.kind, MouseEventKind::Down(_)) {\n                        app.error_popup = None;\n                        app.invalidate();\n                    }\n                }\n                Event::Mouse(mouse) if app.show_help => match mouse.kind {\n                    MouseEventKind::ScrollUp => {\n                        app.help_scroll = app.help_scroll.saturating_sub(1);\n                        app.invalidate();\n                    }\n                    MouseEventKind::ScrollDown => {\n                        app.help_scroll = app.help_scroll.saturating_add(1);\n                        app.invalidate();\n                    }\n                    _ => {}\n                },\n                Event::Mouse(mouse) => match mouse.kind {\n                    MouseEventKind::Up(_) => {\n                        if let Some(ref mut mbt) = app.mbt_state {\n                            mbt.map_drag_last = None;\n                        }\n                        if app.resizing.take().is_some() {\n                            app.invalidate();\n                        }\n                    }\n                    MouseEventKind::Moved | MouseEventKind::Drag(_) => {\n                        if let Some(handle) = app.resizing {\n                            let area = terminal.get_frame().area();\n                            let la = left_area.unwrap_or_default();\n                            match handle {\n                                ResizeHandle::LeftRight => {\n                                    app.left_pct = pct_at(mouse.column, area.x, area.width);\n                                }\n                                ResizeHandle::FeaturesProperties => {\n                                    app.features_pct = pct_at(mouse.row, la.y, la.height);\n                                }\n                                ResizeHandle::PropertiesGeometry => {\n                                    if let Some(pa) = props_area {\n                                        app.properties_pct = pct_at(mouse.row, pa.y, pa.height);\n                                    }\n                                }\n                                ResizeHandle::FileBrowserLeftRight => {\n                                    app.file_left_pct = pct_at(mouse.column, area.x, area.width);\n                                }\n                                ResizeHandle::FileBrowserPreviewFilter => {\n                                    if let Some(rc) = right_col {\n                                        app.file_preview_pct = file_browser_preview_pct_clamped(\n                                            pct_at(mouse.row, rc.y, rc.height),\n                                            app.file_filter_pct,\n                                        );\n                                    }\n                                }\n                                ResizeHandle::FileBrowserFilterInfo => {\n                                    if let Some(rc) = right_col {\n                                        app.file_filter_pct = file_browser_filter_pct_clamped(\n                                            pct_at(mouse.row, rc.y, rc.height)\n                                                .saturating_sub(app.file_preview_pct),\n                                            app.file_preview_pct,\n                                        );\n                                    }\n                                }\n                            }\n                            app.invalidate();\n                            continue;\n                        }\n                        if app.mode == ViewMode::MbtilesMap\n                            && let MouseEventKind::Drag(MouseButton::Left) = mouse.kind\n                            && let (Some(area), Some(ref mut mbt)) =\n                                (map_area, app.mbt_state.as_mut())\n                            && let Some((lc, lr)) = mbt.map_drag_last\n                        {\n                            let dc = i32::from(mouse.column) - i32::from(lc);\n                            let dr = i32::from(mouse.row) - i32::from(lr);\n                            if dc != 0 || dr != 0 {\n                                mbt.pan_by_pixels(area.width, area.height, dc, dr);\n                                mbt.map_drag_last = Some((mouse.column, mouse.row));\n                                app.needs_redraw = true;\n                            }\n                        }\n                        let prev = app.hovered.clone();\n                        app.hovered = None;\n\n                        if app.mode == ViewMode::LayerOverview {\n                            let hover_ok = !matches!(\n                                app.tree_items.get(app.selected_index),\n                                Some(TreeItem::Feature { layer, feat })\n                                    if !app.expanded_features.contains(&(*layer, *feat))\n                            );\n                            if hover_ok\n                                && let Some(area) = tree_area\n                                && let Some(row) = click_row_in_area(\n                                    mouse.column,\n                                    mouse.row,\n                                    area,\n                                    app.tree_scroll as usize,\n                                )\n                                && let Some((l, f, p)) =\n                                    app.tree_items.get(row).and_then(TreeItem::layer_feat_part)\n                            {\n                                app.hovered = Some(HoveredInfo::new(row, l, f, p));\n                            }\n                            if app.hovered.is_none()\n                                && let Some(area) = map_area\n                                && point_in_rect(mouse.column, mouse.row, area)\n                            {\n                                let b = app.get_bounds();\n                                let rx = f64::from(mouse.column - area.x) / f64::from(area.width);\n                                let ry = f64::from(mouse.row - area.y) / f64::from(area.height);\n                                let cx = b.0 + rx * (b.2 - b.0);\n                                let cy = b.3 - ry * (b.3 - b.1);\n                                app.find_hovered_feature(cx, cy, b);\n                            }\n                        }\n                        if app.hovered != prev {\n                            let allow = last_hover_redraw\n                                .is_none_or(|t| t.elapsed() >= HOVER_REDRAW_THROTTLE);\n                            if allow {\n                                last_hover_redraw = Some(Instant::now());\n                                app.invalidate();\n                            }\n                        }\n\n                        // MbtilesMap: update hover on mouse move over the map.\n                        if app.mode == ViewMode::MbtilesMap\n                            && let Some(area) = map_area\n                            && point_in_rect(mouse.column, mouse.row, area)\n                            && let Some(ref mut mbt) = app.mbt_state\n                        {\n                            let rx = f64::from(mouse.column - area.x) / f64::from(area.width);\n                            let ry = f64::from(mouse.row - area.y) / f64::from(area.height);\n                            let (wx, wy) = mbt.viewport_world_at_fracs(rx, ry);\n                            let prev_hov = mbt.hovered.clone();\n                            mbt.find_hovered(wx, wy);\n                            if mbt.hovered != prev_hov {\n                                let allow = last_hover_redraw\n                                    .is_none_or(|t| t.elapsed() >= HOVER_REDRAW_THROTTLE);\n                                if allow {\n                                    last_hover_redraw = Some(Instant::now());\n                                    app.invalidate();\n                                }\n                            }\n                        }\n                    }\n                    MouseEventKind::ScrollUp | MouseEventKind::ScrollDown => {\n                        let up = matches!(mouse.kind, MouseEventKind::ScrollUp);\n                        let s = app.scroll_step();\n                        let step = u16::try_from(s)?;\n\n                        // MbtilesMap: scroll zooms in/out centred on the cursor.\n                        if app.mode == ViewMode::MbtilesMap\n                            && let (Some(area), Some(ref mut mbt)) =\n                                (map_area, app.mbt_state.as_mut())\n                        {\n                            if point_in_rect(mouse.column, mouse.row, area) {\n                                let rx = f64::from(mouse.column - area.x) / f64::from(area.width);\n                                let ry = f64::from(mouse.row - area.y) / f64::from(area.height);\n                                let (wx, wy) = mbt.viewport_world_at_fracs(rx, ry);\n                                mbt.zoom_wheel_at(wx, wy, up);\n                                app.needs_redraw = true;\n                                continue;\n                            }\n                            // Properties panel scroll\n                            if left_area.is_some_and(|a| point_in_rect(mouse.column, mouse.row, a))\n                            {\n                                app.properties_scroll = scroll_by(app.properties_scroll, step, up);\n                                app.invalidate();\n                                continue;\n                            }\n                        }\n\n                        if app.mode == ViewMode::FileBrowser {\n                            if filter_area\n                                .is_some_and(|a| point_in_rect(mouse.column, mouse.row, a))\n                            {\n                                app.filter_scroll = scroll_by(app.filter_scroll, step, up);\n                                app.invalidate();\n                                continue;\n                            }\n                            if info_area.is_some_and(|a| point_in_rect(mouse.column, mouse.row, a))\n                            {\n                                app.file_info_scroll = scroll_by(app.file_info_scroll, step, up);\n                                app.invalidate();\n                                continue;\n                            }\n                        }\n                        if app.mode == ViewMode::LayerOverview {\n                            if props_area.is_some_and(|a| point_in_rect(mouse.column, mouse.row, a))\n                            {\n                                app.properties_scroll = scroll_by(app.properties_scroll, step, up);\n                                app.invalidate();\n                                continue;\n                            }\n                            if let Some(area) = tree_area\n                                && point_in_rect(mouse.column, mouse.row, area)\n                            {\n                                if up {\n                                    app.tree_scroll = app.tree_scroll.saturating_sub(step);\n                                } else {\n                                    let inner = area.height.saturating_sub(2) as usize;\n                                    let max =\n                                        u16::try_from(app.tree_items.len().saturating_sub(inner))?;\n                                    app.tree_scroll = app.tree_scroll.saturating_add(step).min(max);\n                                }\n                                app.invalidate();\n                                continue;\n                            }\n                            if map_area.is_some_and(|a| point_in_rect(mouse.column, mouse.row, a)) {\n                                continue;\n                            }\n                        }\n                        if up {\n                            app.move_up_by(s);\n                        } else {\n                            app.move_down_by(s);\n                        }\n                    }\n                    MouseEventKind::Down(btn) => {\n                        if app.mode == ViewMode::MbtilesMap\n                            && btn == MouseButton::Left\n                            && let (Some(area), Some(ref mut mbt)) =\n                                (map_area, app.mbt_state.as_mut())\n                            && point_in_rect(mouse.column, mouse.row, area)\n                        {\n                            mbt.map_drag_last = Some((mouse.column, mouse.row));\n                            continue;\n                        }\n                        if app.mode == ViewMode::FileBrowser {\n                            if let Some(fl) = file_left {\n                                let dx = fl.x + fl.width;\n                                if mouse.column >= dx.saturating_sub(DIVIDER_GRAB)\n                                    && mouse.column < dx.saturating_add(DIVIDER_GRAB)\n                                    && mouse.row >= fl.y\n                                    && mouse.row < fl.y + fl.height\n                                {\n                                    app.resizing = Some(ResizeHandle::FileBrowserLeftRight);\n                                    app.invalidate();\n                                    continue;\n                                }\n                            }\n                            if let (Some(pa), Some(fa)) = (preview_area, filter_area) {\n                                let mut invalidate = |value: u16, resizer: ResizeHandle| {\n                                    if mouse.row >= value.saturating_sub(DIVIDER_GRAB)\n                                        && mouse.row < value.saturating_add(DIVIDER_GRAB)\n                                        && right_col.is_some_and(|rc| {\n                                            point_in_rect(mouse.column, mouse.row, rc)\n                                        })\n                                    {\n                                        app.resizing = Some(resizer);\n                                        app.invalidate();\n                                        true\n                                    } else {\n                                        false\n                                    }\n                                };\n\n                                let dy_preview = pa.y + pa.height;\n                                if invalidate(dy_preview, ResizeHandle::FileBrowserPreviewFilter) {\n                                    continue;\n                                }\n                                let dy_filter = fa.y + fa.height;\n                                if invalidate(dy_filter, ResizeHandle::FileBrowserFilterInfo) {\n                                    continue;\n                                }\n                            }\n                            if let Some(fa) = filter_area\n                                && point_in_rect(mouse.column, mouse.row, fa)\n                            {\n                                let row = mouse.row.saturating_sub(fa.y + 1) as usize\n                                    + app.filter_scroll as usize;\n                                handle_filter_click(app, row);\n                                continue;\n                            }\n                            if let Some(ia) = info_area\n                                && point_in_rect(mouse.column, mouse.row, ia)\n                                && app.filtered_file_indices.is_empty()\n                                && !app.files.is_empty()\n                            {\n                                let row = mouse.row.saturating_sub(ia.y + 1) as usize;\n                                if row == 2 {\n                                    app.ext_filters.clear();\n                                    app.geom_filters.clear();\n                                    app.algo_filters.clear();\n                                    app.rebuild_filtered_files();\n                                }\n                                continue;\n                            }\n                            if let Some(area) = app.file_table_area {\n                                if app.data_loaded()\n                                    && let Some(widths) = app.file_table_widths\n                                    && let Some(c) = file_header_click_column(\n                                        area,\n                                        &widths,\n                                        mouse.column,\n                                        mouse.row,\n                                    )\n                                {\n                                    app.handle_file_header_click(c);\n                                    continue;\n                                }\n                                if let Some(row) = click_row_in_area(\n                                    mouse.column,\n                                    mouse.row,\n                                    area,\n                                    app.file_list_state.offset(),\n                                ) && row < app.filtered_file_indices.len()\n                                {\n                                    let dbl = last_file_click.is_some_and(|(t, prev)| {\n                                        prev == row && t.elapsed().as_millis() < 400\n                                    });\n                                    last_file_click = Some((Instant::now(), row));\n                                    app.selected_file_index = row;\n                                    app.file_list_state.select(Some(row));\n                                    app.invalidate_bounds();\n                                    if dbl {\n                                        app.handle_enter();\n                                    }\n                                }\n                            }\n                        } else if app.mode == ViewMode::LayerOverview {\n                            if let (Some(la), Some(ta)) = (left_area, tree_area)\n                                && let Some(h) =\n                                    divider_hit(mouse.column, mouse.row, la, ta, geom_area)\n                            {\n                                app.resizing = Some(h);\n                                app.invalidate();\n                                continue;\n                            }\n                            if let Some(area) = tree_area\n                                && let Some(row) = click_row_in_area(\n                                    mouse.column,\n                                    mouse.row,\n                                    area,\n                                    app.tree_scroll as usize,\n                                )\n                                && row < app.tree_items.len()\n                            {\n                                let dbl = last_tree_click.is_some_and(|(t, prev)| {\n                                    prev == row && t.elapsed().as_millis() < 400\n                                });\n                                last_tree_click = Some((Instant::now(), row));\n                                if let Some((l, f, p)) =\n                                    app.tree_items.get(row).and_then(TreeItem::layer_feat_part)\n                                {\n                                    app.handle_feature_click(l, f, p, area.height);\n                                } else {\n                                    app.selected_index = row;\n                                    app.scroll_selected_into_view(\n                                        area.height.saturating_sub(2) as usize\n                                    );\n                                }\n                                app.invalidate_bounds();\n                                if dbl {\n                                    app.handle_enter();\n                                }\n                            }\n                            if let Some(ref h) = app.hovered\n                                && let Some(ta) = tree_area\n                                && map_area\n                                    .is_some_and(|m| point_in_rect(mouse.column, mouse.row, m))\n                            {\n                                app.handle_feature_click(h.layer, h.feat, h.part, ta.height);\n                                app.invalidate_bounds();\n                            }\n                        }\n                    }\n                    _ => {}\n                },\n                Event::Resize(_, _) => app.invalidate(),\n                _ => {}\n            }\n        }\n    }\n    Ok(())\n}\n\n/// Apply scroll delta: subtract if up, add if down.\nfn scroll_by(val: u16, step: u16, up: bool) -> u16 {\n    if up {\n        val.saturating_sub(step)\n    } else {\n        val.saturating_add(step)\n    }\n}\n\n// --- Filtering ---\n\nfn handle_filter_click(app: &mut App, row: usize) {\n    let exts = collect_extensions(&app.files);\n    let geoms = collect_file_geometries(&app.files);\n    let algos = collect_file_algorithms(&app.files);\n\n    let ext_start = 3;\n    let ext_end = ext_start + exts.len();\n    let geom_start = ext_end + 2;\n    let geom_end = geom_start + geoms.len();\n    let algo_start = geom_end + 2;\n    let algo_end = algo_start + algos.len();\n\n    if row == 0 {\n        app.ext_filters.clear();\n        app.geom_filters.clear();\n        app.algo_filters.clear();\n    } else if row >= ext_start && row < ext_end {\n        toggle_set_string(&mut app.ext_filters, &exts[row - ext_start]);\n    } else if row >= geom_start && row < geom_end {\n        toggle_set(&mut app.geom_filters, geoms[row - geom_start]);\n    } else if row >= algo_start && row < algo_end {\n        toggle_set(&mut app.algo_filters, algos[row - algo_start]);\n    }\n    app.rebuild_filtered_files();\n}\n\nfn toggle_set<T: Eq + std::hash::Hash>(set: &mut HashSet<T>, val: T) {\n    if !set.remove(&val) {\n        set.insert(val);\n    }\n}\n\nfn toggle_set_string(set: &mut HashSet<String>, val: &str) {\n    if !set.remove(val) {\n        set.insert(val.to_string());\n    }\n}\n\npub(crate) fn collect_file_geometries(files: &[LsRow]) -> Vec<GeometryType> {\n    let mut set = HashSet::new();\n    for row in files {\n        if let LsRow::Info { info, .. } = row {\n            for g in &info.geometries {\n                set.insert(*g);\n            }\n        }\n    }\n    let mut v: Vec<_> = set.into_iter().collect();\n    v.sort_unstable();\n    v\n}\n\npub(crate) fn collect_file_algorithms(files: &[LsRow]) -> Vec<FileAlgorithm> {\n    let mut set = HashSet::new();\n    for row in files {\n        if let LsRow::Info { info, .. } = row {\n            for a in &info.algorithms {\n                set.insert(*a);\n            }\n        }\n    }\n    let mut v: Vec<_> = set.into_iter().collect();\n    v.sort_by_key(FileAlgorithm::to_string);\n    v\n}\n\nfn collect_extensions(files: &[LsRow]) -> Vec<String> {\n    let mut set = HashSet::new();\n    for row in files {\n        if let Some(ext) = row.path().extension().and_then(|e| e.to_str()) {\n            set.insert(ext.to_lowercase());\n        }\n    }\n    let mut v: Vec<_> = set.into_iter().collect();\n    v.sort_unstable();\n    v\n}\n\n// --- Geometry helpers ---\n\nfn geometry_type_name(geom: &Geometry<i32>) -> &'static str {\n    GeometryType::try_from(geom).map_or(\"Unknown\", Into::into)\n}\n\nfn geometry_color(geom: &Geometry<i32>) -> Color {\n    match geom {\n        Geometry::<i32>::MultiPoint(_) => CLR_MULTI_POINT,\n        Geometry::<i32>::LineString(_) => CLR_LINE,\n        Geometry::<i32>::MultiLineString(_) => CLR_MULTI_LINE,\n        Geometry::<i32>::Polygon(_) | Geometry::<i32>::MultiPolygon(_) if has_bad_winding(geom) => {\n            CLR_BAD_WINDING\n        }\n        Geometry::<i32>::Polygon(_) => CLR_POLYGON,\n        Geometry::<i32>::MultiPolygon(_) => CLR_MULTI_POLYGON,\n        Geometry::<i32>::Point(_)\n        | Geometry::<i32>::Line(_)\n        | Geometry::<i32>::GeometryCollection(_)\n        | Geometry::<i32>::Rect(_)\n        | Geometry::<i32>::Triangle(_) => CLR_POINT,\n    }\n}\n\nfn multi_part_count(geom: &Geometry<i32>) -> usize {\n    match geom {\n        Geometry::<i32>::MultiPoint(mp) => mp.0.len(),\n        Geometry::<i32>::MultiLineString(mls) => mls.0.len(),\n        Geometry::<i32>::MultiPolygon(mpoly) => mpoly.0.len(),\n        _ => 0,\n    }\n}\n\nfn poly_ring_stats(poly: &Polygon<i32>) -> (usize, usize) {\n    let ring_count = 1 + poly.interiors().len();\n    let total_verts =\n        poly.exterior().0.len() + poly.interiors().iter().map(|r| r.0.len()).sum::<usize>();\n    (total_verts, ring_count)\n}\n\nfn feature_suffix(geom: &Geometry<i32>) -> String {\n    let n = multi_part_count(geom);\n    if n > 0 {\n        return format!(\" ({n} parts)\");\n    }\n    match geom {\n        Geometry::<i32>::LineString(ls) => format!(\" ({}v)\", ls.0.len()),\n        Geometry::<i32>::Polygon(poly) => {\n            let (total, ring_count) = poly_ring_stats(poly);\n            if ring_count > 1 {\n                format!(\" ({total}v, {ring_count} rings)\")\n            } else {\n                format!(\" ({total}v)\")\n            }\n        }\n        _ => String::new(),\n    }\n}\n\nfn sub_feature_suffix(geom: &Geometry<i32>, part: usize) -> String {\n    match geom {\n        Geometry::<i32>::MultiLineString(mls) => mls\n            .0\n            .get(part)\n            .map_or(String::new(), |ls| format!(\" ({}v)\", ls.0.len())),\n        Geometry::<i32>::MultiPolygon(mpoly) => mpoly.0.get(part).map_or(String::new(), |poly| {\n            let (total, ring_count) = poly_ring_stats(poly);\n            if ring_count > 1 {\n                format!(\" ({total}v, {ring_count} rings)\")\n            } else {\n                format!(\" ({total}v)\")\n            }\n        }),\n        _ => String::new(),\n    }\n}\n\nfn is_entry_visible(layer: usize, feat: usize, sel: &TreeItem) -> bool {\n    match sel {\n        TreeItem::All => true,\n        TreeItem::Layer(l) => *l == layer,\n        TreeItem::Feature { layer: l, feat: f }\n        | TreeItem::SubFeature {\n            layer: l, feat: f, ..\n        } => *l == layer && *f == feat,\n    }\n}\n\nfn part_color(sel: Option<usize>, hov: Option<usize>, idx: usize, base: Color) -> Color {\n    if sel == Some(idx) {\n        CLR_SELECTED\n    } else if hov == Some(idx) {\n        CLR_HOVERED\n    } else if sel.is_some() || hov.is_some() {\n        CLR_DIMMED\n    } else {\n        base\n    }\n}\n\n// --- Winding ---\n\nfn ring_signed_area(ring: &[Coord<i32>]) -> f64 {\n    let mut area = 0.0;\n    for w in ring.windows(2) {\n        let [x1, y1] = coord_f64(w[0]);\n        let [x2, y2] = coord_f64(w[1]);\n        area += (x2 - x1) * (y2 + y1);\n    }\n    if let (Some(&last), Some(&first)) = (ring.last(), ring.first()) {\n        let [lx, ly] = coord_f64(last);\n        let [fx, fy] = coord_f64(first);\n        area += (fx - lx) * (fy + ly);\n    }\n    area\n}\n\npub(crate) fn is_ring_ccw(ring: &[Coord<i32>]) -> bool {\n    ring_signed_area(ring) < 0.0\n}\n\nfn has_bad_winding(geom: &Geometry<i32>) -> bool {\n    let check = |poly: &Polygon<i32>| {\n        !is_ring_ccw(&poly.exterior().0) || poly.interiors().iter().any(|r| is_ring_ccw(&r.0))\n    };\n    match geom {\n        Geometry::<i32>::Polygon(poly) => check(poly),\n        Geometry::<i32>::MultiPolygon(mpoly) => mpoly.iter().any(check),\n        _ => false,\n    }\n}\n\n// --- Spatial index ---\n\nstruct GeometryIndexEntry {\n    layer: usize,\n    feat: usize,\n    part: Option<usize>,\n    vertices: Vec<[f64; 2]>,\n}\n\nimpl RTreeObject for GeometryIndexEntry {\n    type Envelope = AABB<[f64; 2]>;\n\n    fn envelope(&self) -> Self::Envelope {\n        if self.vertices.is_empty() {\n            return AABB::from_point([0.0, 0.0]);\n        }\n        let (min_x, min_y, max_x, max_y) = self.vertices.iter().fold(\n            (\n                f64::INFINITY,\n                f64::INFINITY,\n                f64::NEG_INFINITY,\n                f64::NEG_INFINITY,\n            ),\n            |(ax, ay, bx, by), v| (ax.min(v[0]), ay.min(v[1]), bx.max(v[0]), by.max(v[1])),\n        );\n        AABB::from_corners([min_x, min_y], [max_x, max_y])\n    }\n}\n\nimpl PointDistance for GeometryIndexEntry {\n    fn distance_2(&self, point: &[f64; 2]) -> f64 {\n        self.vertices\n            .iter()\n            .map(|v| {\n                let dx = v[0] - point[0];\n                let dy = v[1] - point[1];\n                dx * dx + dy * dy\n            })\n            .fold(f64::INFINITY, f64::min)\n    }\n}\n\n#[must_use]\npub fn coord_f64(c: Coord<i32>) -> [f64; 2] {\n    [f64::from(c.x), f64::from(c.y)]\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/rendering/files.rs",
    "content": "use std::collections::HashSet;\n\nuse ratatui::Frame;\nuse ratatui::layout::{Alignment, Constraint, Rect};\nuse ratatui::prelude::{Line, Span, Style};\nuse ratatui::widgets::{Cell, HighlightSpacing, Paragraph, Row, Table, Wrap};\nuse size_format::SizeFormatterSI;\n\nuse crate::ls::{LsRow, NA, na, path_display, row_cells_6};\nuse crate::ui::rendering::map;\nuse crate::ui::state::App;\nuse crate::ui::{\n    CLR_DIMMED, CLR_HINT, CLR_HOVERED, STYLE_BOLD, STYLE_LABEL, STYLE_SELECTED, block_with_title,\n    collect_extensions, collect_file_algorithms, collect_file_geometries,\n};\n\npub fn render_tile_preview_panel(f: &mut Frame<'_>, area: Rect, app: &App) {\n    if let Some(ref fc) = app.preview_fc {\n        map::render_tile_preview(f, area, fc, app.preview_extent);\n    } else {\n        let msg = if app\n            .get_selected_file()\n            .and_then(|r| {\n                app.preview_load_requested\n                    .as_ref()\n                    .filter(|p| p.as_path() == r.path())\n            })\n            .is_some()\n        {\n            \"Loading…\"\n        } else {\n            \"Select a tile file (.mlt / .mvt) to preview\"\n        };\n        f.render_widget(\n            Paragraph::new(Line::from(msg)).block(block_with_title(\"Tile Preview\")),\n            area,\n        );\n    }\n}\n\npub fn render_file_browser(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    app.file_table_area = Some(area);\n    app.file_table_inner_height = area.height.saturating_sub(3) as usize;\n\n    let base = app.file_browser_base.as_deref();\n    let file_w = app\n        .files\n        .iter()\n        .map(|r| path_display(r.path(), base).len())\n        .max()\n        .unwrap_or(4)\n        .max(4);\n\n    let widths = [\n        Constraint::Length(u16::try_from(file_w).unwrap_or_default().min(200)),\n        Constraint::Length(8),\n        Constraint::Length(7),\n        Constraint::Length(6),\n        Constraint::Length(10),\n        Constraint::Min(0),\n    ];\n    app.file_table_widths = Some(widths);\n\n    let header = Row::new(vec![\n        Cell::from(\"File\"),\n        Cell::from(Line::from(\"Size\").alignment(Alignment::Right)),\n        Cell::from(Line::from(\"Enc %\").alignment(Alignment::Right)),\n        Cell::from(Line::from(\"Layers\").alignment(Alignment::Right)),\n        Cell::from(Line::from(\"Features\").alignment(Alignment::Right)),\n        Cell::from(\"Notes\"),\n    ])\n    .style(STYLE_BOLD);\n\n    let rows: Vec<Row> = app\n        .filtered_file_indices\n        .iter()\n        .map(|&i| Row::new(row_cells_6(&app.files[i], base).map(Cell::from)))\n        .collect();\n\n    let sort_hint = if app.data_loaded() {\n        \" Click header to sort\"\n    } else {\n        \"\"\n    };\n    let filtered = app.filtered_file_indices.len();\n    let total = app.files.len();\n    let count = if filtered < total {\n        format!(\"{filtered}/{total}\")\n    } else {\n        total.to_string()\n    };\n    let title =\n        format!(\"MLT Files ({count} found) - ↑/↓ navigate, Enter open, h help, q quit{sort_hint}\");\n    let table = Table::new(rows, widths)\n        .header(header)\n        .column_spacing(1)\n        .block(block_with_title(title))\n        .row_highlight_style(STYLE_SELECTED)\n        .highlight_symbol(\">> \")\n        .highlight_spacing(HighlightSpacing::Always);\n    f.render_stateful_widget(table, area, &mut app.file_list_state);\n}\n\npub fn render_file_filter_panel(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    let exts = collect_extensions(&app.files);\n    let geoms = collect_file_geometries(&app.files);\n    let algos = collect_file_algorithms(&app.files);\n    let has_any =\n        !app.ext_filters.is_empty() || !app.geom_filters.is_empty() || !app.algo_filters.is_empty();\n\n    let selected_mlt = app.get_selected_file();\n    let sel_ext: Option<String> = selected_mlt\n        .and_then(|r| r.path().extension().and_then(|e| e.to_str()))\n        .map(str::to_lowercase);\n    let sel_info = selected_mlt.and_then(|r| match r {\n        LsRow::Info { info, .. } => Some(info),\n        _ => None,\n    });\n\n    let mut lines: Vec<Line<'static>> = Vec::new();\n    let reset_style = if has_any {\n        STYLE_SELECTED\n    } else {\n        Style::default().fg(CLR_DIMMED)\n    };\n    lines.push(Line::from(Span::styled(\"[Reset filters]\", reset_style)));\n    lines.push(Line::from(\"\"));\n\n    let check = |on: bool| if on { \"[x] \" } else { \"[ ] \" };\n    let present_style =\n        |yes: bool| -> Style { Style::default().fg(if yes { CLR_HOVERED } else { CLR_DIMMED }) };\n\n    if !exts.is_empty() {\n        lines.push(Line::from(Span::styled(\"Extensions:\", STYLE_BOLD)));\n        for ext in &exts {\n            lines.push(Line::from(Span::styled(\n                format!(\"  {}{ext}\", check(app.ext_filters.contains(ext))),\n                present_style(sel_ext.as_deref() == Some(ext.as_str())),\n            )));\n        }\n    }\n    if !geoms.is_empty() {\n        lines.push(Line::from(\"\"));\n        lines.push(Line::from(Span::styled(\"Geometry Types:\", STYLE_BOLD)));\n\n        let sel_geoms: HashSet<_> = sel_info\n            .map(|i| i.geometries.iter().copied().collect())\n            .unwrap_or_default();\n        for g in &geoms {\n            lines.push(Line::from(Span::styled(\n                format!(\"  {}{g}\", check(app.geom_filters.contains(g))),\n                present_style(sel_geoms.contains(g)),\n            )));\n        }\n    }\n    if !algos.is_empty() {\n        lines.push(Line::from(\"\"));\n        lines.push(Line::from(Span::styled(\"Algorithms:\", STYLE_BOLD)));\n\n        let sel_algos: HashSet<_> = sel_info\n            .map(|i| i.algorithms.iter().copied().collect())\n            .unwrap_or_default();\n        for a in &algos {\n            lines.push(Line::from(Span::styled(\n                format!(\"  {}{a}\", check(app.algo_filters.contains(a))),\n                present_style(sel_algos.contains(a)),\n            )));\n        }\n    }\n    if lines.is_empty() {\n        lines.push(Line::from(\"(loading…)\"));\n    }\n\n    let inner = area.height.saturating_sub(2);\n    let max = u16::try_from(lines.len().saturating_sub(inner as usize)).unwrap_or(0);\n    app.filter_scroll = app.filter_scroll.min(max);\n    let para = Paragraph::new(lines)\n        .block(block_with_title(\"Filter (click to toggle)\"))\n        .scroll((app.filter_scroll, 0));\n    f.render_widget(para, area);\n}\n\npub fn render_file_info_panel(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    let info = app.get_selected_file().and_then(|r| match r {\n        LsRow::Info { info, .. } => Some(info),\n        _ => None,\n    });\n\n    let lines: Vec<Line<'static>> = if let Some(info) = info {\n        let sz = |n: usize| format!(\"{:.1}B\", SizeFormatterSI::new(n as u64));\n        let row = |name: &str, val: String, desc: &str| -> Line<'static> {\n            let mut spans = vec![\n                Span::styled(format!(\"{name}: \"), STYLE_LABEL),\n                Span::raw(val),\n            ];\n            if !desc.is_empty() {\n                spans.push(Span::styled(\n                    format!(\"  {desc}\"),\n                    Style::default().fg(CLR_HINT),\n                ));\n            }\n            Line::from(spans)\n        };\n        vec![\n            row(\"File\", info.path.clone(), \"\"),\n            row(\"Size\", sz(info.size), \"raw MLT file size\"),\n            row(\n                \"Encoding\",\n                na(info.encoding_pct.map(|p| format!(\"{p:.1}%\"))),\n                \"MLT / (data + metadata)\",\n            ),\n            row(\"Data\", na(info.data_size.map(&sz)), \"decoded payload size\"),\n            row(\n                \"Metadata\",\n                match (info.meta_size, info.meta_pct) {\n                    (Some(m), Some(p)) => format!(\"{} ({:.1}% of data)\", sz(m), p),\n                    _ => NA.to_string(),\n                },\n                \"encoding overhead\",\n            ),\n            row(\"Layers\", info.layers.to_string(), \"tile layer count\"),\n            row(\n                \"Features\",\n                info.features.to_string(),\n                \"total across all layers\",\n            ),\n            row(\n                \"Streams\",\n                na(info.streams.map(|n| n.to_string())),\n                \"encoded data streams\",\n            ),\n            row(\n                \"Geometries\",\n                info.geometries_display(),\n                \"geometry types present\",\n            ),\n            row(\n                \"Algorithms\",\n                info.algorithms_display(),\n                \"compression methods\",\n            ),\n        ]\n    } else if app.filtered_file_indices.is_empty() && !app.files.is_empty() {\n        vec![\n            Line::from(\"No files match the current filters.\"),\n            Line::from(\"\"),\n            Line::from(Span::styled(\"[Reset filters]\", STYLE_SELECTED)),\n        ]\n    } else {\n        vec![Line::from(\"Select a file to view details\")]\n    };\n\n    let inner = area.height.saturating_sub(2) as usize;\n    let max = u16::try_from(lines.len().saturating_sub(inner)).unwrap_or(0);\n    app.file_info_scroll = app.file_info_scroll.min(max);\n\n    let para = Paragraph::new(lines)\n        .block(block_with_title(\"File Info\"))\n        .wrap(Wrap { trim: false })\n        .scroll((app.file_info_scroll, 0));\n    f.render_widget(para, area);\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/rendering/help.rs",
    "content": "use ratatui::Frame;\nuse ratatui::layout::{Alignment, Rect};\nuse ratatui::style::{Color, Style};\nuse ratatui::text::{Line, Span};\nuse ratatui::widgets::{Block, Borders, Padding, Paragraph, Wrap};\n\nuse crate::ui::state::{App, ViewMode};\nuse crate::ui::{\n    CLR_BAD_WINDING, CLR_DIMMED, CLR_EXTENT, CLR_HOVERED, CLR_INNER_RING, CLR_INNER_RING_SEL,\n    CLR_LINE, CLR_MULTI_LINE, CLR_MULTI_POINT, CLR_MULTI_POLYGON, CLR_POINT, CLR_POLYGON,\n    CLR_SELECTED, STYLE_LABEL, STYLE_SELECTED, block_with_title,\n};\n\nconst CLR_ERROR: Color = Color::Red;\n\npub fn render_error_popup(f: &mut Frame<'_>, app: &App) {\n    let Some((ref filename, ref msg)) = app.error_popup else {\n        return;\n    };\n    let area = f.area();\n    let lines: Vec<Line<'_>> = msg\n        .trim()\n        .lines()\n        .map(|s| Line::from(s.to_string()))\n        .collect();\n    let line_count = lines.len();\n    let height = u16::try_from(line_count)\n        .unwrap_or(u16::MAX)\n        .saturating_add(5)\n        .min(28)\n        .min(area.height.saturating_sub(4));\n    let width = 80.min(area.width.saturating_sub(8));\n    let popup = Rect::new(\n        area.x + (area.width.saturating_sub(width)) / 2,\n        area.y + (area.height.saturating_sub(height)) / 2,\n        width,\n        height,\n    );\n    f.render_widget(ratatui::widgets::Clear, popup);\n    let error_block = Block::default()\n        .borders(Borders::ALL)\n        .border_style(Style::default().fg(CLR_ERROR))\n        .title_style(\n            Style::default()\n                .fg(CLR_ERROR)\n                .add_modifier(ratatui::style::Modifier::BOLD),\n        )\n        .title_top(format!(\" Unable to open {filename} \"))\n        .title_bottom(Line::from(\"any key to close\").right_aligned())\n        .padding(Padding::uniform(1));\n    let para = Paragraph::new(lines)\n        .block(error_block)\n        .alignment(Alignment::Center)\n        .wrap(Wrap { trim: true });\n    f.render_widget(para, popup);\n}\n\npub fn render_help_overlay(f: &mut Frame<'_>, app: &mut App) {\n    let area = f.area();\n    let lines = match app.mode {\n        ViewMode::FileBrowser => help_file_browser(),\n        ViewMode::LayerOverview => help_layer_overview(),\n        ViewMode::MbtilesMap => help_mbtiles_map(),\n    };\n    let height = u16::try_from(lines.len())\n        .unwrap_or(u16::MAX)\n        .saturating_add(2)\n        .min(area.height.saturating_sub(2));\n    let width = 62.min(area.width.saturating_sub(4));\n    let popup = Rect::new(\n        area.x + (area.width.saturating_sub(width)) / 2,\n        area.y + (area.height.saturating_sub(height)) / 2,\n        width,\n        height,\n    );\n    let inner = height.saturating_sub(2);\n    let max = u16::try_from(lines.len().saturating_sub(inner as usize)).unwrap_or(0);\n    app.help_scroll = app.help_scroll.min(max);\n    f.render_widget(ratatui::widgets::Clear, popup);\n    let para = Paragraph::new(lines)\n        .block(block_with_title(\n            \"Help (↑/↓/scroll to navigate, any other key to close)\",\n        ))\n        .scroll((app.help_scroll, 0));\n    f.render_widget(para, popup);\n}\n\nfn heading(text: &str) -> Line<'static> {\n    Line::from(Span::styled(text.to_string(), STYLE_SELECTED))\n}\n\nfn key(k: &str, desc: &str) -> Line<'static> {\n    Line::from(vec![\n        Span::styled(format!(\"  {k:<20}\"), STYLE_LABEL),\n        Span::raw(desc.to_string()),\n    ])\n}\n\nfn color(c: Color, label: &str, desc: &str) -> Line<'static> {\n    Line::from(vec![\n        Span::raw(\"  \"),\n        Span::styled(format!(\"{label:<20}\"), Style::default().fg(c)),\n        Span::raw(desc.to_string()),\n    ])\n}\n\nfn help_file_browser() -> Vec<Line<'static>> {\n    vec![\n        heading(\"Keyboard\"),\n        key(\"?  h  F1\", \"Toggle this help\"),\n        key(\"q  Ctrl+c\", \"Quit\"),\n        key(\"Up/Down  j/k\", \"Navigate file list\"),\n        key(\"PageUp/PageDown\", \"Scroll by page\"),\n        key(\"Home/End\", \"Jump to first/last\"),\n        key(\"Enter\", \"Open selected MLT file\"),\n        key(\"Esc\", \"Quit\"),\n        Line::from(\"\"),\n        heading(\"Mouse\"),\n        key(\"Click row\", \"Select file\"),\n        key(\"Double-click row\", \"Open file\"),\n        key(\"Click header\", \"Sort by column\"),\n        key(\"Scroll\", \"Navigate file list\"),\n        key(\"Drag divider\", \"Resize panels\"),\n        Line::from(\"\"),\n        heading(\"Filter Panel\"),\n        key(\"Click checkbox\", \"Toggle geometry/algorithm filter\"),\n        key(\"Click [Reset]\", \"Clear all filters\"),\n    ]\n}\n\nfn help_mbtiles_map() -> Vec<Line<'static>> {\n    vec![\n        heading(\"Keyboard\"),\n        key(\"?  h  F1\", \"Toggle this help\"),\n        key(\"q  Ctrl+c  Esc\", \"Quit\"),\n        Line::from(\"\"),\n        heading(\"Mouse\"),\n        key(\"Scroll on map\", \"Zoom ±0.5 levels (centred on cursor)\"),\n        key(\"Left-drag on map\", \"Pan\"),\n        key(\"Hover over map\", \"Inspect feature properties in left panel\"),\n        Line::from(\"\"),\n        heading(\"Map Colors\"),\n        color(CLR_POINT, \"Magenta\", \"Point\"),\n        color(CLR_LINE, \"Cyan\", \"LineString\"),\n        color(CLR_POLYGON, \"Blue\", \"Polygon\"),\n        color(CLR_EXTENT, \"Dark gray\", \"Tile boundaries\"),\n        color(CLR_HOVERED, \"White\", \"Hovered feature\"),\n    ]\n}\n\nfn help_layer_overview() -> Vec<Line<'static>> {\n    vec![\n        heading(\"Keyboard\"),\n        key(\"?  h  F1\", \"Toggle this help\"),\n        key(\"q  Ctrl+c\", \"Quit\"),\n        key(\"Esc\", \"Back to file browser\"),\n        key(\"Up/Down  j/k\", \"Navigate feature tree\"),\n        key(\"PageUp/PageDown\", \"Scroll by page\"),\n        key(\"Home/End\", \"Jump to first/last\"),\n        key(\"Enter\", \"Expand/collapse layer or feature\"),\n        key(\"+  =  Right\", \"Expand selected node\"),\n        key(\"-\", \"Collapse (or jump to parent)\"),\n        key(\"*\", \"Expand/collapse all layers\"),\n        key(\"Left\", \"Jump to parent node\"),\n        key(\"Ctrl+h / Ctrl+l\", \"Resize left/right split\"),\n        key(\"Shift+J / Shift+K\", \"Resize top/bottom split\"),\n        Line::from(\"\"),\n        heading(\"Mouse\"),\n        key(\"Click tree item\", \"Select (drill into level)\"),\n        key(\"Double-click\", \"Expand/collapse\"),\n        key(\"Hover tree/map\", \"Highlight geometry\"),\n        key(\"Click on map\", \"Select hovered feature\"),\n        key(\"Scroll panels\", \"Scroll tree/properties\"),\n        key(\"Drag dividers\", \"Resize panels\"),\n        Line::from(\"\"),\n        heading(\"Map Colors\"),\n        color(CLR_POINT, \"Magenta\", \"Point\"),\n        color(CLR_MULTI_POINT, \"Light magenta\", \"MultiPoint\"),\n        color(CLR_LINE, \"Cyan\", \"LineString\"),\n        color(CLR_MULTI_LINE, \"Light cyan\", \"MultiLineString\"),\n        color(CLR_POLYGON, \"Blue\", \"Polygon (outer ring, CCW)\"),\n        color(CLR_MULTI_POLYGON, \"Light blue\", \"MultiPolygon\"),\n        color(CLR_INNER_RING, \"Red\", \"Inner ring (hole, CW)\"),\n        color(CLR_BAD_WINDING, \"Light red\", \"Non-standard winding\"),\n        color(CLR_EXTENT, \"Dark gray\", \"Tile extent boundary\"),\n        Line::from(\"\"),\n        heading(\"Selection Colors\"),\n        color(CLR_SELECTED, \"Yellow\", \"Selected feature/part\"),\n        color(CLR_HOVERED, \"White\", \"Hovered feature\"),\n        color(CLR_INNER_RING_SEL, \"Salmon\", \"Inner ring (selected)\"),\n        color(CLR_DIMMED, \"Dark gray\", \"Sibling parts (dimmed)\"),\n    ]\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/rendering/layers.rs",
    "content": "use mlt_core::geo_types::{\n    Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,\n};\nuse mlt_core::geojson::Feature;\nuse ratatui::Frame;\nuse ratatui::layout::{Constraint, Direction, Layout, Rect};\nuse ratatui::prelude::{Line, Modifier, Span, Style};\nuse ratatui::widgets::{Paragraph, Wrap};\n\nuse crate::ui::mbt::MbtTileData;\nuse crate::ui::state::{App, TreeItem, ViewMode};\nuse crate::ui::{\n    CLR_HOVERED_TREE, STYLE_LABEL, STYLE_SELECTED, block_with_title, feature_suffix,\n    geometry_color, geometry_type_name, is_ring_ccw, stat_line, sub_feature_suffix,\n};\n\npub fn render_tree_panel(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    let lines: Vec<Line<'static>> = app\n        .tree_items\n        .iter()\n        .enumerate()\n        .map(|(idx, item)| {\n            let (text, base) = match item {\n                TreeItem::All => (\"All\".into(), None),\n                TreeItem::Layer(li) => {\n                    let g = &app.layer_groups[*li];\n                    let n = g.feature_indices.len();\n                    let first = g\n                        .feature_indices\n                        .first()\n                        .map(|&gi| geometry_type_name(&app.fc.features[gi].geometry));\n                    let all_same = first.is_some_and(|ft| {\n                        g.feature_indices\n                            .iter()\n                            .all(|&gi| geometry_type_name(&app.fc.features[gi].geometry) == ft)\n                    });\n                    let label = if all_same && n > 0 {\n                        format!(\"{}s\", first.unwrap())\n                    } else {\n                        \"features\".into()\n                    };\n\n                    (\n                        format!(\"  Layer: {} ({n} {label}, extent {})\", g.name, g.extent),\n                        None,\n                    )\n                }\n                TreeItem::Feature { layer, feat } => {\n                    let geom = &app.feature(*layer, *feat).geometry;\n                    (\n                        format!(\n                            \"    Feat {feat}: {}{}\",\n                            geometry_type_name(geom),\n                            feature_suffix(geom)\n                        ),\n                        Some(geometry_color(geom)),\n                    )\n                }\n                TreeItem::SubFeature { layer, feat, part } => {\n                    let geom = &app.feature(*layer, *feat).geometry;\n                    let n = match geom {\n                        Geometry::<i32>::MultiPoint(_) => \"Point\",\n                        Geometry::<i32>::MultiLineString(_) => \"LineString\",\n                        Geometry::<i32>::MultiPolygon(_) => \"Polygon\",\n                        _ => \"Part\",\n                    };\n                    (\n                        format!(\"      Part {part}: {n}{}\", sub_feature_suffix(geom, *part)),\n                        Some(geometry_color(geom)),\n                    )\n                }\n            };\n\n            let style = if idx == app.selected_index {\n                STYLE_SELECTED\n            } else if app.hovered.as_ref().is_some_and(|h| h.tree_idx == idx) {\n                Style::default()\n                    .fg(CLR_HOVERED_TREE)\n                    .add_modifier(Modifier::UNDERLINED)\n            } else {\n                base.map_or(Style::default(), |c| Style::default().fg(c))\n            };\n            Line::from(vec![\n                Span::raw(if idx == app.selected_index {\n                    \">> \"\n                } else {\n                    \"   \"\n                }),\n                Span::styled(text, style),\n            ])\n        })\n        .collect();\n\n    let title = match app.mode {\n        ViewMode::LayerOverview => {\n            let name = app\n                .current_file\n                .as_ref()\n                .and_then(|p| p.file_name())\n                .and_then(|n| n.to_str())\n                .unwrap_or(\"unknown\");\n            format!(\"{name} - Enter/+/-:expand, Esc:back, h:help, q:quit\")\n        }\n        ViewMode::FileBrowser | ViewMode::MbtilesMap => \"Features\".into(),\n    };\n    let inner = area.height.saturating_sub(2) as usize;\n    app.tree_inner_height = inner;\n    let max = u16::try_from(app.tree_items.len().saturating_sub(inner)).unwrap_or(0);\n    app.tree_scroll = app.tree_scroll.min(max);\n    let para = Paragraph::new(lines)\n        .block(block_with_title(title))\n        .scroll((app.tree_scroll, 0));\n    f.render_widget(para, area);\n}\n\nfn feature_property_lines(feat: &Feature) -> Vec<Line<'static>> {\n    let lines: Vec<Line<'static>> = feat\n        .properties\n        .iter()\n        .filter(|(k, _)| *k != \"_layer\" && *k != \"_extent\")\n        .map(|(k, v)| {\n            Line::from(vec![\n                Span::styled(format!(\"{k}: \"), STYLE_LABEL),\n                Span::raw(match v {\n                    serde_json::Value::String(s) => s.clone(),\n                    _ => v.to_string(),\n                }),\n            ])\n        })\n        .collect();\n    if lines.is_empty() {\n        vec![Line::from(\"(no properties)\")]\n    } else {\n        lines\n    }\n}\n\npub fn render_properties_panel(f: &mut Frame<'_>, area: Rect, app: &mut App) -> Rect {\n    let chunks = Layout::default()\n        .direction(Direction::Vertical)\n        .constraints([\n            Constraint::Percentage(app.properties_pct),\n            Constraint::Percentage(100u16.saturating_sub(app.properties_pct)),\n        ])\n        .split(area);\n\n    render_properties_top(f, chunks[0], app);\n    render_geometry_stats(f, chunks[1], app);\n    chunks[1]\n}\n\nfn render_properties_top(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    let item = app.tree_items.get(app.selected_index);\n    let hov = app.hovered.as_ref();\n    let (title, lines): (String, Vec<Line<'static>>) = match item {\n        None | Some(TreeItem::All | TreeItem::Layer(_)) => {\n            if let Some(h) = hov {\n                let key = (h.layer, h.feat);\n                if app.last_properties_key != Some(key) {\n                    app.properties_scroll = 0;\n                    app.last_properties_key = Some(key);\n                }\n                (\n                    format!(\"Properties (feat {}, hover)\", h.feat),\n                    feature_property_lines(app.feature(h.layer, h.feat)),\n                )\n            } else {\n                app.last_properties_key = None;\n                (\n                    \"Properties\".into(),\n                    vec![Line::from(\n                        \"Select or hover over a feature to view properties\",\n                    )],\n                )\n            }\n        }\n        Some(TreeItem::Feature { layer, feat } | TreeItem::SubFeature { layer, feat, .. }) => {\n            let key = (*layer, *feat);\n            if app.last_properties_key != Some(key) {\n                app.properties_scroll = 0;\n                app.last_properties_key = Some(key);\n            }\n            (\n                format!(\"Properties (feat {feat})\"),\n                feature_property_lines(app.feature(*layer, *feat)),\n            )\n        }\n    };\n    let inner = area.height.saturating_sub(2);\n    let max = u16::try_from(lines.len().saturating_sub(inner as usize)).unwrap_or(0);\n    app.properties_scroll = app.properties_scroll.min(max);\n    let para = Paragraph::new(lines)\n        .block(block_with_title(title))\n        .wrap(Wrap { trim: true })\n        .scroll((app.properties_scroll, 0));\n    f.render_widget(para, area);\n}\n\nfn info_point(lines: &mut Vec<Line<'static>>, p: Point<i32>) {\n    lines.push(stat_line(\"Coords\", &format!(\"{:?}\", <[i32; 2]>::from(p))));\n}\n\nfn info_line_string(lines: &mut Vec<Line<'static>>, ls: &LineString<i32>) {\n    lines.push(stat_line(\"Vertices\", &ls.0.len()));\n}\n\nfn info_polygon(lines: &mut Vec<Line<'static>>, poly: &Polygon<i32>) {\n    let total: usize =\n        poly.exterior().0.len() + poly.interiors().iter().map(|r| r.0.len()).sum::<usize>();\n    lines.push(stat_line(\"Vertices\", &total));\n    lines.push(stat_line(\"Rings\", &(1 + poly.interiors().len())));\n    let ext = &poly.exterior().0;\n    let w = if is_ring_ccw(ext) { \"CCW\" } else { \"CW\" };\n    lines.push(Line::from(format!(\"  Ring 0: {}v, {w}\", ext.len())));\n    for (i, ring) in poly.interiors().iter().enumerate() {\n        let w = if is_ring_ccw(&ring.0) { \"CCW\" } else { \"CW\" };\n        lines.push(Line::from(format!(\n            \"  Ring {}: {}v, {w}\",\n            i + 1,\n            ring.0.len()\n        )));\n    }\n}\n\nfn info_multi_point(lines: &mut Vec<Line<'static>>, pts: &MultiPoint<i32>) {\n    lines.push(stat_line(\"Points\", &pts.0.len()));\n}\n\nfn info_multi_line_string(lines: &mut Vec<Line<'static>>, mls: &MultiLineString<i32>) {\n    let total: usize = mls.iter().map(|ls| ls.0.len()).sum();\n    lines.push(stat_line(\"Parts\", &mls.0.len()));\n    lines.push(stat_line(\"Vertices\", &total));\n}\n\nfn info_multi_polygon(lines: &mut Vec<Line<'static>>, mpoly: &MultiPolygon<i32>) {\n    let total: usize = mpoly\n        .iter()\n        .flat_map(|p| {\n            std::iter::once(p.exterior().0.len()).chain(p.interiors().iter().map(|r| r.0.len()))\n        })\n        .sum();\n    let total_rings: usize = mpoly.iter().map(|p| 1 + p.interiors().len()).sum();\n    lines.push(stat_line(\"Parts\", &mpoly.0.len()));\n    lines.push(stat_line(\"Total vertices\", &total));\n    lines.push(stat_line(\"Total rings\", &total_rings));\n}\n\nfn geometry_stats_lines(geom: &Geometry<i32>) -> Vec<Line<'static>> {\n    let mut lines = vec![stat_line(\"Type\", &geometry_type_name(geom))];\n    match geom {\n        Geometry::<i32>::Point(p) => info_point(&mut lines, *p),\n        Geometry::<i32>::LineString(ls) => info_line_string(&mut lines, ls),\n        Geometry::<i32>::Polygon(poly) => info_polygon(&mut lines, poly),\n        Geometry::<i32>::MultiPoint(pts) => info_multi_point(&mut lines, pts),\n        Geometry::<i32>::MultiLineString(mls) => info_multi_line_string(&mut lines, mls),\n        Geometry::<i32>::MultiPolygon(mpoly) => info_multi_polygon(&mut lines, mpoly),\n        _ => unreachable!(\"Unexpected geometry type {geom:?}\"),\n    }\n    lines\n}\n\nfn subpart_stats_lines(geom: &Geometry<i32>, part: usize) -> Vec<Line<'static>> {\n    let mut lines = vec![stat_line(\n        \"Component\",\n        &format!(\"part #{} of a {}\", part, geometry_type_name(geom)),\n    )];\n    match geom {\n        Geometry::<i32>::MultiPoint(pts) => {\n            if let Some(p) = pts.0.get(part) {\n                lines.push(stat_line(\"Type\", &\"Point\"));\n                info_point(&mut lines, *p);\n            }\n        }\n        Geometry::<i32>::MultiLineString(mls) => {\n            if let Some(ls) = mls.0.get(part) {\n                lines.push(stat_line(\"Type\", &\"LineString\"));\n                info_line_string(&mut lines, ls);\n            }\n        }\n        Geometry::<i32>::MultiPolygon(mpoly) => {\n            if let Some(poly) = mpoly.0.get(part) {\n                lines.push(stat_line(\"Type\", &\"Polygon\"));\n                info_polygon(&mut lines, poly);\n            }\n        }\n        _ => {}\n    }\n    lines\n}\n\n// ---------------------------------------------------------------------------\n// MBTiles hover properties panel\n// ---------------------------------------------------------------------------\n\n/// Renders the left panel for `MbtilesMap` mode: shows hovered feature properties only.\npub fn render_mbtiles_hover_panel(f: &mut Frame<'_>, area: Rect, app: &mut App) {\n    let (title, lines) = mbt_hover_title_and_lines(app);\n    let inner = area.height.saturating_sub(2) as usize;\n    let max = u16::try_from(lines.len().saturating_sub(inner)).unwrap_or(0);\n    app.properties_scroll = app.properties_scroll.min(max);\n    let para = Paragraph::new(lines)\n        .block(block_with_title(title))\n        .wrap(Wrap { trim: true })\n        .scroll((app.properties_scroll, 0));\n    f.render_widget(para, area);\n}\n\nfn mbt_hover_title_and_lines(app: &App) -> (String, Vec<Line<'static>>) {\n    let Some(ref mbt) = app.mbt_state else {\n        return (\"Properties\".into(), vec![Line::from(\"No mbtiles loaded\")]);\n    };\n    let Some(ref h) = mbt.hovered else {\n        return (\n            \"Properties\".into(),\n            vec![Line::from(\"Hover over a feature to inspect properties\")],\n        );\n    };\n    let tile_entry = mbt.tiles.get(&h.tile);\n    let Some(MbtTileData::Loaded {\n        fc, layer_groups, ..\n    }) = tile_entry\n    else {\n        let msg: String = match tile_entry {\n            Some(MbtTileData::Empty) => \"Tile empty (no vector data)\".into(),\n            Some(MbtTileData::Error(e)) => {\n                let snippet: String = e.chars().take(160).collect();\n                format!(\"Tile error: {snippet}\")\n            }\n            None | Some(MbtTileData::Loading | MbtTileData::Loaded { .. }) => {\n                \"Tile loading…\".into()\n            }\n        };\n        return (\"Properties\".into(), vec![Line::from(msg)]);\n    };\n    let Some(group) = layer_groups.get(h.layer_idx) else {\n        return (\"Properties\".into(), vec![Line::from(\"(feature not found)\")]);\n    };\n    let Some(&gi) = group.feature_indices.get(h.feat_idx) else {\n        return (\"Properties\".into(), vec![Line::from(\"(feature not found)\")]);\n    };\n    let feat = &fc.features[gi];\n    let (z, tx, ty) = h.tile;\n    let title = format!(\n        \"Properties — {} feat {} (tile {z}/{tx}/{ty})\",\n        group.name, h.feat_idx\n    );\n    (title, feature_property_lines(feat))\n}\n\nfn render_geometry_stats(f: &mut Frame<'_>, area: Rect, app: &App) {\n    let item = app.tree_items.get(app.selected_index);\n    let hov = app.hovered.as_ref();\n\n    let lines = match item {\n        Some(TreeItem::SubFeature { layer, feat, part }) => {\n            subpart_stats_lines(&app.feature(*layer, *feat).geometry, *part)\n        }\n        Some(TreeItem::Feature { layer, feat }) => {\n            geometry_stats_lines(&app.feature(*layer, *feat).geometry)\n        }\n        _ => {\n            if let Some(h) = hov {\n                let geom = &app.feature(h.layer, h.feat).geometry;\n                match h.part {\n                    Some(p) => subpart_stats_lines(geom, p),\n                    None => geometry_stats_lines(geom),\n                }\n            } else {\n                vec![Line::from(\n                    \"Select or hover over a feature to view geometry info\",\n                )]\n            }\n        }\n    };\n\n    let para = Paragraph::new(lines)\n        .block(block_with_title(\"Geometry\"))\n        .wrap(Wrap { trim: false });\n    f.render_widget(para, area);\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/rendering/map.rs",
    "content": "use std::collections::HashSet;\n\nuse mlt_core::geo_types::{Coord, Geometry, Polygon};\nuse mlt_core::geojson::FeatureCollection;\nuse ratatui::Frame;\nuse ratatui::layout::Rect;\nuse ratatui::prelude::{Span, Style};\nuse ratatui::style::Color;\nuse ratatui::widgets::canvas::{Canvas, Context, Line as CanvasLine, Rectangle};\n\nuse crate::ui::mbt::{MbtHoveredInfo, MbtTileData, TileTransform};\nuse crate::ui::state::{App, LayerGroup, TreeItem};\nuse crate::ui::{\n    CLR_DIMMED, CLR_EXTENT, CLR_HOVERED, CLR_INNER_RING, CLR_INNER_RING_SEL, CLR_POLYGON,\n    CLR_SELECTED, block_with_title, coord_f64, geometry_color, is_ring_ccw, part_color,\n};\n\npub fn render_map_panel(f: &mut Frame<'_>, area: Rect, app: &App) {\n    let sel = app.selected_item();\n    let ext = app.extent();\n    let (x0, y0, x1, y1) = app.calculate_bounds();\n\n    let canvas = Canvas::default()\n        .block(block_with_title(\"Map View\"))\n        .x_bounds([x0, x1])\n        .y_bounds([y0, y1])\n        .paint(|ctx| {\n            ctx.draw(&Rectangle {\n                x: 0.0,\n                y: 0.0,\n                width: f64::from(ext),\n                height: f64::from(ext),\n                color: CLR_EXTENT,\n            });\n\n            let hov = app.hovered.as_ref();\n            let draw_feat = |ctx: &mut Context<'_>, gi: usize| {\n                let geom = &app.fc.features[gi].geometry;\n                let base = geometry_color(geom);\n                let is_hov = hov.is_some_and(|h| app.global_idx(h.layer, h.feat) == gi);\n                let sel_part = match sel {\n                    TreeItem::SubFeature { layer, feat, part }\n                        if app.global_idx(*layer, *feat) == gi =>\n                    {\n                        Some(*part)\n                    }\n                    _ => None,\n                };\n                let hov_part =\n                    hov.and_then(|h| (app.global_idx(h.layer, h.feat) == gi).then_some(h.part)?);\n                draw_feature(ctx, geom, base, is_hov, sel_part, hov_part);\n            };\n\n            match sel {\n                TreeItem::All => {\n                    for gi in 0..app.fc.features.len() {\n                        draw_feat(ctx, gi);\n                    }\n                }\n                TreeItem::Layer(l) => {\n                    for &gi in &app.layer_groups[*l].feature_indices {\n                        draw_feat(ctx, gi);\n                    }\n                }\n                TreeItem::Feature { layer, feat } | TreeItem::SubFeature { layer, feat, .. } => {\n                    draw_feat(ctx, app.global_idx(*layer, *feat));\n                }\n            }\n        });\n\n    f.render_widget(canvas, area);\n}\n\n/// Full-tile preview for file browser (all layers, no r-tree/mouse).\npub fn render_tile_preview(f: &mut Frame<'_>, area: Rect, fc: &FeatureCollection, extent: u32) {\n    let canvas = Canvas::default()\n        .block(block_with_title(\"Tile Preview\"))\n        .x_bounds([0.0, f64::from(extent)])\n        .y_bounds([0.0, f64::from(extent)])\n        .paint(|ctx| {\n            ctx.draw(&Rectangle {\n                x: 0.0,\n                y: 0.0,\n                width: f64::from(extent),\n                height: f64::from(extent),\n                color: CLR_EXTENT,\n            });\n            for feat in &fc.features {\n                draw_feature(\n                    ctx,\n                    &feat.geometry,\n                    geometry_color(&feat.geometry),\n                    false,\n                    None,\n                    None,\n                );\n            }\n        });\n\n    f.render_widget(canvas, area);\n}\n\nfn draw_feature(\n    ctx: &mut Context<'_>,\n    geom: &Geometry<i32>,\n    base: Color,\n    is_hov: bool,\n    sel_part: Option<usize>,\n    hov_part: Option<usize>,\n) {\n    let color = if is_hov { CLR_HOVERED } else { base };\n    match geom {\n        Geometry::<i32>::Point(p) => draw_point(ctx, p.0, color),\n        Geometry::<i32>::LineString(ls) => draw_line(ctx, &ls.0, color),\n        Geometry::<i32>::Polygon(poly) => draw_polygon(ctx, poly, is_hov, color),\n        Geometry::<i32>::MultiPoint(pts) => {\n            for (i, p) in pts.iter().enumerate() {\n                draw_point(ctx, p.0, part_color(sel_part, hov_part, i, color));\n            }\n        }\n        Geometry::<i32>::MultiLineString(lines) => {\n            for (i, ls) in lines.iter().enumerate() {\n                draw_line(ctx, &ls.0, part_color(sel_part, hov_part, i, color));\n            }\n        }\n        Geometry::<i32>::MultiPolygon(polys) => {\n            for (i, poly) in polys.iter().enumerate() {\n                let pc = part_color(sel_part, hov_part, i, color);\n                draw_polygon(ctx, poly, matches!(pc, CLR_HOVERED | CLR_SELECTED), pc);\n            }\n        }\n        _ => {}\n    }\n}\n\nfn draw_point(ctx: &mut Context<'_>, c: Coord<i32>, color: Color) {\n    let [x, y] = coord_f64(c);\n    ctx.print(x, y, Span::styled(\"×\", Style::default().fg(color)));\n}\n\nfn draw_line(ctx: &mut Context<'_>, coords: &[Coord<i32>], color: Color) {\n    for w in coords.windows(2) {\n        let [x1, y1] = coord_f64(w[0]);\n        let [x2, y2] = coord_f64(w[1]);\n        ctx.draw(&CanvasLine::new(x1, y1, x2, y2, color));\n    }\n}\n\nfn draw_ring(ctx: &mut Context<'_>, ring: &[Coord<i32>], color: Color) {\n    draw_line(ctx, ring, color);\n    if let (Some(&last), Some(&first)) = (ring.last(), ring.first()) {\n        let [lx, ly] = coord_f64(last);\n        let [fx, fy] = coord_f64(first);\n        ctx.draw(&CanvasLine::new(lx, ly, fx, fy, color));\n    }\n}\n\nfn ring_color(ring: &[Coord<i32>], highlighted: bool, fallback: Color) -> Color {\n    if !highlighted {\n        if is_ring_ccw(ring) {\n            CLR_POLYGON\n        } else {\n            CLR_INNER_RING\n        }\n    } else if is_ring_ccw(ring) {\n        fallback\n    } else {\n        CLR_INNER_RING_SEL\n    }\n}\n\nfn draw_polygon(ctx: &mut Context<'_>, poly: &Polygon<i32>, highlighted: bool, fallback: Color) {\n    let ext = &poly.exterior().0;\n    draw_ring(ctx, ext, ring_color(ext, highlighted, fallback));\n    for ring in poly.interiors() {\n        draw_ring(ctx, &ring.0, ring_color(&ring.0, highlighted, fallback));\n    }\n}\n\n// ---------------------------------------------------------------------------\n// MBTiles world-map rendering\n// ---------------------------------------------------------------------------\n\n/// Draw all layers from a decoded tile (`data_tile` is the MVT source key used for hover match).\n#[allow(clippy::too_many_arguments)] // Canvas draw context + tile payload + viewport Y range.\nfn draw_mbtiles_loaded_tile_layers(\n    ctx: &mut Context<'_>,\n    hovered: Option<&MbtHoveredInfo>,\n    data_tile: (u8, u32, u32),\n    fc: &FeatureCollection,\n    extent: u32,\n    layer_groups: &[LayerGroup],\n    vy0: f64,\n    vy1: f64,\n) {\n    let (tz, tx, ty) = data_tile;\n    let transform = TileTransform::new(tz, tx, ty, extent);\n    let hov_gi = hovered.and_then(|h| {\n        if h.tile != (tz, tx, ty) {\n            return None;\n        }\n        layer_groups\n            .get(h.layer_idx)\n            .and_then(|g| g.feature_indices.get(h.feat_idx))\n            .copied()\n    });\n    for group in layer_groups {\n        for &gi in &group.feature_indices {\n            let feat = &fc.features[gi];\n            let base = geometry_color(&feat.geometry);\n            let is_hov = hov_gi == Some(gi);\n            let color = if is_hov { CLR_HOVERED } else { base };\n            draw_geom_world(ctx, &feat.geometry, &transform, vy0, vy1, color);\n        }\n    }\n}\n\n/// Render the interactive world map for an .mbtiles file.\n///\n/// World coordinate space: `x ∈ [0, 1]` west→east, `y ∈ [0, 1]` north→south.\n///\n/// `y_bounds` must be `[min_y, max_y]` with `min_y < max_y` for Ratatui's `Painter` clip math.\n/// The painter maps **larger** world Y toward the **top** of the widget, so we reflect each\n/// geographic `wy` with [`mbt_screen_y`] to get north-up on screen.\npub fn render_mbtiles_map_panel(f: &mut Frame<'_>, area: Rect, app: &App) {\n    let Some(ref mbt) = app.mbt_state else {\n        return;\n    };\n    let visible = mbt.visible_tiles();\n    let (cz, cx, cy) = mbt.center_tile_xyz();\n    let title = format!(\n        \"World Map — {cz}/{cx}/{cy} — zoom {:.1}  drag=pan  hover=info  q/Esc quit\",\n        mbt.zoom_f\n    );\n\n    let canvas = Canvas::default()\n        .block(block_with_title(title))\n        .x_bounds([mbt.vp_x0, mbt.vp_x1])\n        .y_bounds([mbt.vp_y0, mbt.vp_y1])\n        .paint(|ctx| {\n            let vy0 = mbt.vp_y0;\n            let vy1 = mbt.vp_y1;\n\n            // Under native resolution: draw each loaded ancestor at most once (world-aligned).\n            let mut overzoom_drawn: HashSet<(u8, u32, u32)> = HashSet::new();\n            for &(tz, tx, ty) in &visible {\n                if matches!(\n                    mbt.tiles.get(&(tz, tx, ty)),\n                    Some(MbtTileData::Loaded { .. })\n                ) {\n                    continue;\n                }\n                let Some((sz, sx, sy)) = mbt.find_overzoom_source(tz, tx, ty) else {\n                    continue;\n                };\n                if !overzoom_drawn.insert((sz, sx, sy)) {\n                    continue;\n                }\n                let Some(MbtTileData::Loaded {\n                    fc,\n                    extent,\n                    layer_groups,\n                    ..\n                }) = mbt.tiles.get(&(sz, sx, sy))\n                else {\n                    continue;\n                };\n                draw_mbtiles_loaded_tile_layers(\n                    ctx,\n                    mbt.hovered.as_ref(),\n                    (sz, sx, sy),\n                    fc,\n                    *extent,\n                    layer_groups,\n                    vy0,\n                    vy1,\n                );\n            }\n\n            for &(tz, tx, ty) in &visible {\n                let n = f64::from(1u32 << tz);\n                let x0 = f64::from(tx) / n;\n                let y0 = f64::from(ty) / n;\n                let x1 = f64::from(tx + 1) / n;\n                let y1 = f64::from(ty + 1) / n;\n\n                // Draw tile border.\n                draw_world_rect_vp(ctx, vy0, vy1, x0, y0, x1, y1, CLR_EXTENT);\n\n                let Some(tile_data) = mbt.tiles.get(&(tz, tx, ty)) else {\n                    continue;\n                };\n\n                match tile_data {\n                    MbtTileData::Loading => {\n                        let cx = f64::midpoint(x0, x1);\n                        let cy = f64::midpoint(y0, y1);\n                        let sy = mbt_screen_y(vy0, vy1, cy);\n                        ctx.print(cx, sy, Span::styled(\"…\", Style::default().fg(CLR_DIMMED)));\n                    }\n                    MbtTileData::Loaded {\n                        fc,\n                        extent,\n                        layer_groups,\n                        ..\n                    } => {\n                        draw_mbtiles_loaded_tile_layers(\n                            ctx,\n                            mbt.hovered.as_ref(),\n                            (tz, tx, ty),\n                            fc,\n                            *extent,\n                            layer_groups,\n                            vy0,\n                            vy1,\n                        );\n                    }\n                    MbtTileData::Empty | MbtTileData::Error(_) => {}\n                }\n            }\n        });\n\n    f.render_widget(canvas, area);\n}\n\n/// Map geographic world Y to canvas Y so north is at the top of the map widget.\n#[inline]\nfn mbt_screen_y(vp_y0: f64, vp_y1: f64, wy: f64) -> f64 {\n    vp_y0 + vp_y1 - wy\n}\n\n/// Draw the four edges of a world-coordinate tile rectangle (north-up).\n#[allow(clippy::too_many_arguments)] // Ratatui canvas line API; args map 1:1 to world corners + style.\nfn draw_world_rect_vp(\n    ctx: &mut Context<'_>,\n    vp_y0: f64,\n    vp_y1: f64,\n    x0: f64,\n    y0: f64,\n    x1: f64,\n    y1: f64,\n    color: Color,\n) {\n    let s0 = mbt_screen_y(vp_y0, vp_y1, y0);\n    let s1 = mbt_screen_y(vp_y0, vp_y1, y1);\n    ctx.draw(&CanvasLine::new(x0, s0, x1, s0, color));\n    ctx.draw(&CanvasLine::new(x0, s1, x1, s1, color));\n    ctx.draw(&CanvasLine::new(x0, s0, x0, s1, color));\n    ctx.draw(&CanvasLine::new(x1, s0, x1, s1, color));\n}\n\n/// Draw a geometry in world coordinates using the provided tile transform (north-up on canvas).\nfn draw_geom_world(\n    ctx: &mut Context<'_>,\n    geom: &Geometry<i32>,\n    t: &TileTransform,\n    vp_y0: f64,\n    vp_y1: f64,\n    color: Color,\n) {\n    match geom {\n        Geometry::<i32>::Point(p) => {\n            let [wx, wy] = t.to_world(p.0);\n            let sy = mbt_screen_y(vp_y0, vp_y1, wy);\n            ctx.print(wx, sy, Span::styled(\"×\", Style::default().fg(color)));\n        }\n        Geometry::<i32>::LineString(ls) => draw_world_line(ctx, &ls.0, t, vp_y0, vp_y1, color),\n        Geometry::<i32>::Polygon(poly) => {\n            draw_world_ring(ctx, &poly.exterior().0, t, vp_y0, vp_y1, color);\n            for ring in poly.interiors() {\n                draw_world_ring(ctx, &ring.0, t, vp_y0, vp_y1, color);\n            }\n        }\n        Geometry::<i32>::MultiPoint(mp) => {\n            for p in mp.iter() {\n                let [wx, wy] = t.to_world(p.0);\n                let sy = mbt_screen_y(vp_y0, vp_y1, wy);\n                ctx.print(wx, sy, Span::styled(\"×\", Style::default().fg(color)));\n            }\n        }\n        Geometry::<i32>::MultiLineString(mls) => {\n            for ls in mls.iter() {\n                draw_world_line(ctx, &ls.0, t, vp_y0, vp_y1, color);\n            }\n        }\n        Geometry::<i32>::MultiPolygon(mpoly) => {\n            for poly in mpoly.iter() {\n                draw_world_ring(ctx, &poly.exterior().0, t, vp_y0, vp_y1, color);\n                for ring in poly.interiors() {\n                    draw_world_ring(ctx, &ring.0, t, vp_y0, vp_y1, color);\n                }\n            }\n        }\n        _ => {}\n    }\n}\n\nfn draw_world_line(\n    ctx: &mut Context<'_>,\n    coords: &[Coord<i32>],\n    t: &TileTransform,\n    vp_y0: f64,\n    vp_y1: f64,\n    color: Color,\n) {\n    for w in coords.windows(2) {\n        let [xa, ya] = t.to_world(w[0]);\n        let [xb, yb] = t.to_world(w[1]);\n        let sa = mbt_screen_y(vp_y0, vp_y1, ya);\n        let sb = mbt_screen_y(vp_y0, vp_y1, yb);\n        ctx.draw(&CanvasLine::new(xa, sa, xb, sb, color));\n    }\n}\n\nfn draw_world_ring(\n    ctx: &mut Context<'_>,\n    ring: &[Coord<i32>],\n    t: &TileTransform,\n    vp_y0: f64,\n    vp_y1: f64,\n    color: Color,\n) {\n    draw_world_line(ctx, ring, t, vp_y0, vp_y1, color);\n    if let (Some(&last), Some(&first)) = (ring.last(), ring.first()) {\n        let [lx, ly] = t.to_world(last);\n        let [fx, fy] = t.to_world(first);\n        ctx.draw(&CanvasLine::new(\n            lx,\n            mbt_screen_y(vp_y0, vp_y1, ly),\n            fx,\n            mbt_screen_y(vp_y0, vp_y1, fy),\n            color,\n        ));\n    }\n}\n"
  },
  {
    "path": "rust/mlt/src/ui/rendering/mod.rs",
    "content": "pub mod files;\npub mod help;\npub mod layers;\npub mod map;\n"
  },
  {
    "path": "rust/mlt/src/ui/state.rs",
    "content": "use std::collections::HashSet;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc;\nuse std::time::Instant;\n\nuse mlt_core::GeometryType;\nuse mlt_core::geo_types::{Geometry, Polygon};\nuse mlt_core::geojson::{Feature, FeatureCollection};\nuse ratatui::layout::{Constraint, Rect};\nuse ratatui::widgets::TableState;\nuse rstar::{PointDistance as _, RTree};\n\nuse crate::ls::{FileAlgorithm, FileSortColumn, LsRow};\nuse crate::ui::mbt::MbtilesState;\nuse crate::ui::{\n    GeometryIndexEntry, auto_expand, coord_f64, group_by_layer, is_entry_visible, load_fc,\n    multi_part_count,\n};\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ViewMode {\n    FileBrowser,\n    LayerOverview,\n    /// Interactive world map viewer for .mbtiles files.\n    MbtilesMap,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ResizeHandle {\n    LeftRight,\n    FeaturesProperties,\n    PropertiesGeometry,\n    FileBrowserLeftRight,\n    FileBrowserPreviewFilter,\n    FileBrowserFilterInfo,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum TreeItem {\n    All,\n    Layer(usize),\n    Feature {\n        layer: usize,\n        feat: usize,\n    },\n    SubFeature {\n        layer: usize,\n        feat: usize,\n        part: usize,\n    },\n}\n\nimpl TreeItem {\n    pub(crate) fn layer_feat_part(&self) -> Option<(usize, usize, Option<usize>)> {\n        match self {\n            Self::Feature { layer, feat } => Some((*layer, *feat, None)),\n            Self::SubFeature { layer, feat, part } => Some((*layer, *feat, Some(*part))),\n            _ => None,\n        }\n    }\n}\n\n#[derive(Clone, PartialEq, Eq)]\npub struct HoveredInfo {\n    pub(crate) tree_idx: usize,\n    pub(crate) layer: usize,\n    pub(crate) feat: usize,\n    pub(crate) part: Option<usize>,\n}\n\nimpl HoveredInfo {\n    pub fn new(tree_idx: usize, layer: usize, feat: usize, part: Option<usize>) -> Self {\n        Self {\n            tree_idx,\n            layer,\n            feat,\n            part,\n        }\n    }\n}\n\npub struct LayerGroup {\n    pub(crate) name: String,\n    pub(crate) extent: u32,\n    pub(crate) feature_indices: Vec<usize>,\n}\n\nimpl LayerGroup {\n    pub fn new(name: String, extent: u32, feature_indices: Vec<usize>) -> Self {\n        Self {\n            name,\n            extent,\n            feature_indices,\n        }\n    }\n}\n\npub type PreviewValue = (PathBuf, Result<(FeatureCollection, u32), ()>);\n\npub struct App {\n    pub(crate) mode: ViewMode,\n    pub(crate) files: Vec<LsRow>,\n    pub(crate) selected_file_index: usize,\n    pub(crate) file_list_state: TableState,\n    pub(crate) analysis_rx: Option<mpsc::Receiver<Vec<LsRow>>>,\n    /// Base path for file browser; used when drawing to show relative paths.\n    pub(crate) file_browser_base: Option<PathBuf>,\n    file_sort: Option<(FileSortColumn, bool)>,\n    pub(crate) file_table_area: Option<Rect>,\n    pub(crate) file_table_widths: Option<[Constraint; 6]>,\n    pub(crate) current_file: Option<PathBuf>,\n    pub(crate) fc: FeatureCollection,\n    pub(crate) layer_groups: Vec<LayerGroup>,\n    pub(crate) tree_items: Vec<TreeItem>,\n    pub(crate) selected_index: usize,\n    pub(crate) hovered: Option<HoveredInfo>,\n    expanded_layers: Vec<bool>,\n    pub(crate) expanded_features: HashSet<(usize, usize)>,\n    last_scroll_time: Instant,\n    scroll_speed: usize,\n    pub(crate) needs_redraw: bool,\n    cached_bounds: Option<(f64, f64, f64, f64)>,\n    cached_bounds_key: usize,\n    pub(crate) left_pct: u16,\n    pub(crate) features_pct: u16,\n    pub(crate) resizing: Option<ResizeHandle>,\n    pub(crate) properties_scroll: u16,\n    pub(crate) tree_scroll: u16,\n    pub(crate) tree_inner_height: usize,\n    pub(crate) last_properties_key: Option<(usize, usize)>,\n    pub(crate) properties_pct: u16,\n    geometry_index: Option<RTree<GeometryIndexEntry>>,\n    pub(crate) file_left_pct: u16,\n    pub(crate) file_preview_pct: u16,\n    pub(crate) file_filter_pct: u16,\n    pub(crate) ext_filters: HashSet<String>,\n    pub(crate) geom_filters: HashSet<GeometryType>,\n    pub(crate) algo_filters: HashSet<FileAlgorithm>,\n    pub(crate) filter_scroll: u16,\n    pub(crate) filtered_file_indices: Vec<usize>,\n    pub(crate) file_table_inner_height: usize,\n    pub(crate) show_help: bool,\n    pub(crate) help_scroll: u16,\n    pub(crate) error_popup: Option<(String, String)>,\n    pub(crate) file_info_scroll: u16,\n    pub(crate) preview_tile_path: Option<PathBuf>,\n    pub(crate) preview_fc: Option<FeatureCollection>,\n    pub(crate) preview_extent: u32,\n    pub(crate) preview_rx: Option<mpsc::Receiver<PreviewValue>>,\n    pub(crate) preview_load_requested: Option<PathBuf>,\n    /// State for the `MbtilesMap` mode (Some only when mode == `MbtilesMap`).\n    pub(crate) mbt_state: Option<Box<MbtilesState>>,\n}\n\nimpl Default for App {\n    fn default() -> Self {\n        Self {\n            mode: ViewMode::FileBrowser,\n            files: Vec::new(),\n            selected_file_index: 0,\n            file_list_state: TableState::default(),\n            analysis_rx: None,\n            file_browser_base: None,\n            file_sort: None,\n            file_table_area: None,\n            file_table_widths: None,\n            current_file: None,\n            fc: FeatureCollection {\n                features: Vec::new(),\n                ty: \"FeatureCollection\".into(),\n            },\n            layer_groups: Vec::new(),\n            tree_items: Vec::new(),\n            selected_index: 0,\n            hovered: None,\n            expanded_layers: Vec::new(),\n            expanded_features: HashSet::new(),\n            last_scroll_time: Instant::now(),\n            scroll_speed: 1,\n            needs_redraw: true,\n            cached_bounds: None,\n            cached_bounds_key: usize::MAX,\n            left_pct: 30,\n            features_pct: 50,\n            resizing: None,\n            properties_scroll: 0,\n            last_properties_key: None,\n            tree_scroll: 0,\n            tree_inner_height: 0,\n            properties_pct: 50,\n            geometry_index: None,\n            file_left_pct: 70,\n            file_preview_pct: 33,\n            file_filter_pct: 33,\n            ext_filters: HashSet::new(),\n            geom_filters: HashSet::new(),\n            algo_filters: HashSet::new(),\n            filter_scroll: 0,\n            filtered_file_indices: Vec::new(),\n            file_table_inner_height: 10,\n            show_help: false,\n            help_scroll: 0,\n            error_popup: None,\n            file_info_scroll: 0,\n            preview_tile_path: None,\n            preview_fc: None,\n            preview_extent: 4096,\n            preview_rx: None,\n            preview_load_requested: None,\n            mbt_state: None,\n        }\n    }\n}\n\nimpl App {\n    pub(crate) fn new_file_browser(\n        files: Vec<LsRow>,\n        analysis_rx: Option<mpsc::Receiver<Vec<LsRow>>>,\n        base_path: PathBuf,\n    ) -> Self {\n        let mut file_list_state = TableState::default();\n        file_list_state.select(Some(0));\n        let filtered_file_indices = (0..files.len()).collect();\n        Self {\n            files,\n            file_list_state,\n            analysis_rx,\n            file_browser_base: Some(base_path),\n            filtered_file_indices,\n            ..Self::default()\n        }\n    }\n\n    pub(crate) fn new_mbtiles(mbt_state: MbtilesState, path: PathBuf) -> Self {\n        Self {\n            mode: ViewMode::MbtilesMap,\n            current_file: Some(path),\n            mbt_state: Some(Box::new(mbt_state)),\n            ..Self::default()\n        }\n    }\n\n    pub(crate) fn new_single_file(fc: FeatureCollection, path: Option<PathBuf>) -> Self {\n        let layer_groups = group_by_layer(&fc);\n        let expanded_layers = auto_expand(&layer_groups);\n        let mut app = Self {\n            mode: ViewMode::LayerOverview,\n            current_file: path,\n            expanded_layers,\n            layer_groups,\n            fc,\n            ..Self::default()\n        };\n        app.build_geometry_index();\n        app.build_tree_items();\n        app\n    }\n\n    pub(crate) fn data_loaded(&self) -> bool {\n        self.analysis_rx.is_none()\n            && !self\n                .files\n                .iter()\n                .any(|r| matches!(r, LsRow::Loading { .. }))\n    }\n\n    pub(crate) fn open_help(&mut self) {\n        self.show_help = true;\n        self.help_scroll = 0;\n        self.invalidate();\n    }\n\n    pub(crate) fn handle_file_header_click(&mut self, col: FileSortColumn) {\n        if !self.data_loaded() {\n            return;\n        }\n        let prev = self.get_selected_file().map(|r| r.path().to_path_buf());\n        let asc = !matches!(self.file_sort, Some((c, a)) if c == col && a);\n        self.file_sort = Some((col, asc));\n        self.files.sort_by(|a, b| file_cmp(a, b, col, asc));\n        self.rebuild_filtered_files();\n        if let Some(path) = prev\n            && let Some(pos) = self\n                .filtered_file_indices\n                .iter()\n                .position(|&i| self.files[i].path() == path.as_path())\n        {\n            self.selected_file_index = pos;\n            self.file_list_state.select(Some(pos));\n        }\n    }\n\n    fn load_file(&mut self, path: &Path) -> anyhow::Result<()> {\n        self.fc = load_fc(path)?;\n        self.layer_groups = group_by_layer(&self.fc);\n        self.current_file = Some(path.to_path_buf());\n        self.mode = ViewMode::LayerOverview;\n        self.expanded_layers = auto_expand(&self.layer_groups);\n        self.expanded_features.clear();\n        self.build_geometry_index();\n        self.build_tree_items();\n        self.selected_index = 0;\n        self.invalidate_bounds();\n        Ok(())\n    }\n\n    pub(crate) fn global_idx(&self, layer: usize, feat: usize) -> usize {\n        self.layer_groups[layer].feature_indices[feat]\n    }\n\n    pub(crate) fn feature(&self, layer: usize, feat: usize) -> &Feature {\n        &self.fc.features[self.global_idx(layer, feat)]\n    }\n\n    pub(crate) fn extent(&self) -> u32 {\n        self.layer_groups.first().map_or(4096, |g| g.extent)\n    }\n\n    pub(crate) fn selected_item(&self) -> &TreeItem {\n        &self.tree_items[self.selected_index]\n    }\n\n    fn build_tree_items(&mut self) {\n        self.tree_items.clear();\n        self.tree_items.push(TreeItem::All);\n        for (li, group) in self.layer_groups.iter().enumerate() {\n            self.tree_items.push(TreeItem::Layer(li));\n            if !self.expanded_layers.get(li).copied().unwrap_or(false) {\n                continue;\n            }\n            for (fi, &gi) in group.feature_indices.iter().enumerate() {\n                self.tree_items.push(TreeItem::Feature {\n                    layer: li,\n                    feat: fi,\n                });\n                if self.expanded_features.contains(&(li, fi)) {\n                    for part in 0..multi_part_count(&self.fc.features[gi].geometry) {\n                        self.tree_items.push(TreeItem::SubFeature {\n                            layer: li,\n                            feat: fi,\n                            part,\n                        });\n                    }\n                }\n            }\n        }\n    }\n\n    fn build_geometry_index(&mut self) {\n        let mut entries: Vec<GeometryIndexEntry> = Vec::new();\n        for (li, group) in self.layer_groups.iter().enumerate() {\n            for (fi, &gi) in group.feature_indices.iter().enumerate() {\n                let geom = &self.fc.features[gi].geometry;\n                let n = multi_part_count(geom);\n                let parts: Vec<Option<usize>> = if n == 0 {\n                    vec![None]\n                } else {\n                    (0..n).map(Some).collect()\n                };\n                for part in parts {\n                    let vertices = geometry_vertices(geom, part);\n                    if !vertices.is_empty() {\n                        entries.push(GeometryIndexEntry {\n                            layer: li,\n                            feat: fi,\n                            part,\n                            vertices,\n                        });\n                    }\n                }\n            }\n        }\n        self.geometry_index = (!entries.is_empty()).then(|| RTree::bulk_load(entries));\n    }\n\n    pub(crate) fn scroll_step(&mut self) -> usize {\n        let now = Instant::now();\n        let elapsed = now.duration_since(self.last_scroll_time).as_millis();\n        self.last_scroll_time = now;\n        self.scroll_speed = match elapsed {\n            0..50 => (self.scroll_speed + 1).min(20),\n            50..120 => self.scroll_speed.max(2),\n            _ => 1,\n        };\n        self.scroll_speed\n    }\n\n    pub(crate) fn move_up_by(&mut self, n: usize) {\n        self.move_by(n, false);\n    }\n\n    pub(crate) fn move_down_by(&mut self, n: usize) {\n        self.move_by(n, true);\n    }\n\n    fn move_by(&mut self, n: usize, down: bool) {\n        match self.mode {\n            ViewMode::FileBrowser => {\n                let prev = self.selected_file_index;\n                let max = self.filtered_file_indices.len().saturating_sub(1);\n                self.selected_file_index = if down {\n                    self.selected_file_index.saturating_add(n).min(max)\n                } else {\n                    self.selected_file_index.saturating_sub(n).min(max)\n                };\n                self.file_list_state.select(Some(self.selected_file_index));\n                if self.selected_file_index != prev {\n                    self.invalidate_bounds();\n                }\n            }\n            ViewMode::LayerOverview => {\n                let prev = self.selected_index;\n                let max = self.tree_items.len().saturating_sub(1);\n                self.selected_index = if down {\n                    self.selected_index.saturating_add(n).min(max)\n                } else {\n                    self.selected_index.saturating_sub(n)\n                };\n                if self.selected_index != prev {\n                    self.scroll_selected_into_view(self.tree_inner_height);\n                    self.invalidate_bounds();\n                }\n            }\n            ViewMode::MbtilesMap => {}\n        }\n    }\n\n    pub(crate) fn move_to_start(&mut self) {\n        self.move_up_by(usize::MAX);\n    }\n\n    pub(crate) fn move_to_end(&mut self) {\n        self.move_down_by(usize::MAX);\n    }\n\n    pub(crate) fn page_size(&self) -> usize {\n        match self.mode {\n            ViewMode::FileBrowser => self.file_table_inner_height,\n            ViewMode::LayerOverview | ViewMode::MbtilesMap => self.tree_inner_height,\n        }\n    }\n\n    pub(crate) fn handle_enter(&mut self) {\n        match self.mode {\n            ViewMode::FileBrowser => {\n                let path = self.get_selected_file().map(|r| r.path().to_path_buf());\n                let (path_str, is_error, error_msg) = self\n                    .get_selected_file()\n                    .map(|r| {\n                        let s = r.path().display().to_string();\n                        match r {\n                            LsRow::Error { error, .. } => (s, true, error.clone()),\n                            _ => (s, false, String::new()),\n                        }\n                    })\n                    .unwrap_or_default();\n                if let Some(path) = path {\n                    if is_error {\n                        self.error_popup = Some((path_str, error_msg));\n                        self.invalidate();\n                    } else if let Err(e) = self.load_file(&path) {\n                        self.error_popup = Some((path_str, e.to_string()));\n                        self.invalidate();\n                    }\n                }\n            }\n            ViewMode::LayerOverview => match self.tree_items.get(self.selected_index) {\n                Some(TreeItem::Layer(li)) => {\n                    let li = *li;\n                    if li < self.expanded_layers.len() {\n                        self.expanded_layers[li] = !self.expanded_layers[li];\n                        self.build_tree_items();\n                        self.invalidate();\n                    }\n                }\n                Some(TreeItem::Feature { layer, feat }) => {\n                    let key = (*layer, *feat);\n                    if multi_part_count(&self.feature(key.0, key.1).geometry) > 0 {\n                        if !self.expanded_features.remove(&key) {\n                            self.expanded_features.insert(key);\n                        }\n                        self.build_tree_items();\n                        self.invalidate();\n                    }\n                }\n                _ => {}\n            },\n            ViewMode::MbtilesMap => {}\n        }\n    }\n\n    pub(crate) fn handle_plus(&mut self) {\n        if self.mode != ViewMode::LayerOverview {\n            return;\n        }\n        match self.tree_items.get(self.selected_index) {\n            Some(TreeItem::Layer(li)) => {\n                let li = *li;\n                if li < self.expanded_layers.len() && !self.expanded_layers[li] {\n                    self.expanded_layers[li] = true;\n                    self.build_tree_items();\n                    self.invalidate();\n                }\n            }\n            Some(TreeItem::Feature { layer, feat }) => {\n                let key = (*layer, *feat);\n                if multi_part_count(&self.feature(key.0, key.1).geometry) > 0\n                    && !self.expanded_features.contains(&key)\n                {\n                    self.expanded_features.insert(key);\n                    self.build_tree_items();\n                    self.invalidate();\n                }\n            }\n            _ => {}\n        }\n    }\n\n    pub(crate) fn handle_minus(&mut self) {\n        if self.mode != ViewMode::LayerOverview {\n            return;\n        }\n        match self.tree_items.get(self.selected_index).cloned() {\n            Some(TreeItem::Layer(li))\n                if li < self.expanded_layers.len() && self.expanded_layers[li] =>\n            {\n                self.expanded_layers[li] = false;\n                self.rebuild_and_clamp();\n            }\n            Some(TreeItem::Feature { layer, feat }) => {\n                if self.expanded_features.remove(&(layer, feat)) {\n                    self.rebuild_and_clamp();\n                } else if layer < self.expanded_layers.len() && self.expanded_layers[layer] {\n                    self.expanded_layers[layer] = false;\n                    self.rebuild_and_select(|it| matches!(it, TreeItem::Layer(l) if *l == layer));\n                }\n            }\n            Some(TreeItem::SubFeature { layer, feat, .. }) => {\n                self.expanded_features.remove(&(layer, feat));\n                self.rebuild_and_select(|it| {\n                    matches!(it, TreeItem::Feature { layer: l, feat: f } if *l == layer && *f == feat)\n                });\n            }\n            _ => {}\n        }\n    }\n\n    pub(crate) fn handle_star(&mut self) {\n        if self.mode != ViewMode::LayerOverview {\n            return;\n        }\n        let new = !self.expanded_layers.iter().all(|&e| e);\n        self.expanded_layers.fill(new);\n        self.rebuild_and_clamp();\n    }\n\n    pub(crate) fn handle_escape(&mut self) -> bool {\n        match self.mode {\n            ViewMode::FileBrowser | ViewMode::MbtilesMap => true,\n            ViewMode::LayerOverview if self.files.is_empty() => true,\n            ViewMode::LayerOverview => {\n                self.mode = ViewMode::FileBrowser;\n                self.invalidate_bounds();\n                false\n            }\n        }\n    }\n\n    pub(crate) fn handle_left_arrow(&mut self) {\n        if self.mode != ViewMode::LayerOverview {\n            return;\n        }\n        let Some(item) = self.tree_items.get(self.selected_index).cloned() else {\n            return;\n        };\n        let target = match item {\n            TreeItem::SubFeature { layer, feat, .. } => self.tree_items.iter().position(|t| {\n                matches!(t, TreeItem::Feature { layer: l, feat: f } if *l == layer && *f == feat)\n            }),\n            TreeItem::Feature { layer, .. } => self\n                .tree_items\n                .iter()\n                .position(|t| matches!(t, TreeItem::Layer(l) if *l == layer)),\n            TreeItem::Layer(_) => Some(0),\n            TreeItem::All => {\n                if !self.files.is_empty() {\n                    self.mode = ViewMode::FileBrowser;\n                }\n                return;\n            }\n        };\n        if let Some(idx) = target\n            && idx != self.selected_index\n        {\n            self.selected_index = idx;\n            self.invalidate_bounds();\n        }\n    }\n\n    fn rebuild_and_clamp(&mut self) {\n        self.build_tree_items();\n        self.selected_index = self\n            .selected_index\n            .min(self.tree_items.len().saturating_sub(1));\n        self.invalidate_bounds();\n    }\n\n    fn rebuild_and_select(&mut self, pred: impl Fn(&TreeItem) -> bool) {\n        self.build_tree_items();\n        if let Some(idx) = self.tree_items.iter().position(pred) {\n            self.selected_index = idx;\n        }\n        self.invalidate_bounds();\n    }\n\n    pub(crate) fn invalidate(&mut self) {\n        self.needs_redraw = true;\n    }\n\n    pub(crate) fn invalidate_bounds(&mut self) {\n        self.cached_bounds = None;\n        self.invalidate();\n    }\n\n    pub(crate) fn selected_file_real_index(&self) -> Option<usize> {\n        self.filtered_file_indices\n            .get(self.selected_file_index)\n            .copied()\n    }\n\n    pub(crate) fn get_selected_file(&self) -> Option<&LsRow> {\n        self.selected_file_real_index()\n            .and_then(|i| self.files.get(i))\n    }\n\n    pub(crate) fn rebuild_filtered_files(&mut self) {\n        let prev = self.selected_file_real_index();\n        let has_filters = !self.ext_filters.is_empty()\n            || !self.geom_filters.is_empty()\n            || !self.algo_filters.is_empty();\n        self.filtered_file_indices = (0..self.files.len())\n            .filter(|&i| {\n                if !has_filters {\n                    return true;\n                }\n                let ext = self.files[i]\n                    .path()\n                    .extension()\n                    .and_then(|e| e.to_str())\n                    .map(str::to_lowercase)\n                    .unwrap_or_default();\n                if !self.ext_filters.is_empty()\n                    && !self.ext_filters.iter().any(|f| f.as_str() == ext)\n                {\n                    return false;\n                }\n                match &self.files[i] {\n                    LsRow::Info { info, .. } => {\n                        self.geom_filters\n                            .iter()\n                            .all(|g| info.geometries.contains(g))\n                            && self\n                                .algo_filters\n                                .iter()\n                                .all(|a| info.algorithms.contains(a))\n                    }\n                    _ => true,\n                }\n            })\n            .collect();\n        let pos = prev\n            .and_then(|ri| self.filtered_file_indices.iter().position(|&i| i == ri))\n            .unwrap_or(0);\n        self.selected_file_index = pos;\n        self.file_list_state.select(Some(pos));\n        self.invalidate();\n    }\n\n    pub(crate) fn get_bounds(&mut self) -> (f64, f64, f64, f64) {\n        if self.cached_bounds_key != self.selected_index || self.cached_bounds.is_none() {\n            self.cached_bounds = Some(self.calculate_bounds());\n            self.cached_bounds_key = self.selected_index;\n        }\n        self.cached_bounds.unwrap()\n    }\n\n    pub fn calculate_bounds(&self) -> (f64, f64, f64, f64) {\n        let sel = self.selected_item();\n        let ext = self.extent();\n        let (mut x0, mut y0) = (f64::INFINITY, f64::INFINITY);\n        let (mut x1, mut y1) = (f64::NEG_INFINITY, f64::NEG_INFINITY);\n\n        let mut update = |v: &[f64; 2]| {\n            x0 = x0.min(v[0]);\n            y0 = y0.min(v[1]);\n            x1 = x1.max(v[0]);\n            y1 = y1.max(v[1]);\n        };\n\n        let geoms: Vec<&Geometry<i32>> = match sel {\n            TreeItem::All => self.fc.features.iter().map(|f| &f.geometry).collect(),\n            TreeItem::Layer(l) => self.layer_groups[*l]\n                .feature_indices\n                .iter()\n                .map(|&gi| &self.fc.features[gi].geometry)\n                .collect(),\n            TreeItem::Feature { layer, feat } | TreeItem::SubFeature { layer, feat, .. } => {\n                vec![&self.feature(*layer, *feat).geometry]\n            }\n        };\n        for geom in geoms {\n            for v in geometry_vertices(geom, None) {\n                update(&v);\n            }\n        }\n\n        x0 = x0.min(0.0);\n        y0 = y0.min(0.0);\n        x1 = x1.max(f64::from(ext));\n        y1 = y1.max(f64::from(ext));\n        let px = (x1 - x0) * 0.1;\n        let py = (y1 - y0) * 0.1;\n        (x0 - px, y0 - py, x1 + px, y1 + py)\n    }\n\n    pub(crate) fn find_hovered_feature(&mut self, cx: f64, cy: f64, bounds: (f64, f64, f64, f64)) {\n        let sel = self.selected_item().clone();\n        let threshold = (bounds.2 - bounds.0).max(bounds.3 - bounds.1) * 0.02;\n        let thresh_sq = threshold * threshold;\n        let early_exit = thresh_sq * 0.01;\n        let pt = [cx, cy];\n\n        let best = if let Some(ref tree) = self.geometry_index {\n            let mut best: Option<(f64, usize, usize, Option<usize>)> = None;\n            for e in tree.nearest_neighbor_iter(&pt) {\n                let d = e.distance_2(&pt);\n                if d > thresh_sq {\n                    break;\n                }\n                if !is_entry_visible(e.layer, e.feat, &sel) {\n                    continue;\n                }\n                if best.is_none_or(|(bd, ..)| d < bd) {\n                    best = Some((d, e.layer, e.feat, e.part));\n                    if d < early_exit {\n                        break;\n                    }\n                }\n            }\n            best\n        } else {\n            None\n        };\n\n        self.hovered = best.and_then(|(_, l, f, p)| {\n            self.find_tree_idx_for_feature(l, f, p)\n                .map(|idx| HoveredInfo::new(idx, l, f, p))\n        });\n    }\n\n    fn find_tree_idx_for_feature(\n        &self,\n        layer: usize,\n        feat: usize,\n        part: Option<usize>,\n    ) -> Option<usize> {\n        for (idx, item) in self.tree_items.iter().enumerate() {\n            match item {\n                TreeItem::Layer(li)\n                    if *li == layer\n                        && !self.expanded_layers.get(layer).copied().unwrap_or(false) =>\n                {\n                    return Some(idx);\n                }\n                TreeItem::Feature { layer: l, feat: f }\n                    if *l == layer\n                        && *f == feat\n                        && (part.is_none() || !self.expanded_features.contains(&(layer, feat))) =>\n                {\n                    return Some(idx);\n                }\n                TreeItem::SubFeature {\n                    layer: l,\n                    feat: f,\n                    part: p,\n                } if *l == layer && *f == feat && part == Some(*p) => {\n                    return Some(idx);\n                }\n                _ => {}\n            }\n        }\n        None\n    }\n\n    fn ensure_layer_expanded(&mut self, layer: usize) {\n        if layer < self.expanded_layers.len() && !self.expanded_layers[layer] {\n            self.expanded_layers[layer] = true;\n            self.build_tree_items();\n        }\n    }\n\n    fn select_and_scroll(\n        &mut self,\n        layer: usize,\n        feat: Option<usize>,\n        part: Option<usize>,\n        tree_height: u16,\n    ) {\n        let inner = tree_height.saturating_sub(2) as usize;\n        self.ensure_layer_expanded(layer);\n        if let Some(f) = feat {\n            if multi_part_count(&self.feature(layer, f).geometry) > 0 {\n                self.expanded_features.insert((layer, f));\n                self.build_tree_items();\n            }\n            if let Some(idx) = self.find_tree_idx_for_feature(layer, f, part) {\n                self.selected_index = idx;\n                self.scroll_selected_into_view(inner);\n            }\n        } else if let Some(idx) = self\n            .tree_items\n            .iter()\n            .position(|it| matches!(it, TreeItem::Layer(l) if *l == layer))\n        {\n            self.selected_index = idx;\n            self.scroll_selected_into_view(inner);\n        }\n        self.invalidate_bounds();\n    }\n\n    pub(crate) fn scroll_selected_into_view(&mut self, inner_height: usize) {\n        let idx = self.selected_index;\n        if idx < self.tree_scroll as usize {\n            self.tree_scroll = u16::try_from(idx).unwrap_or(0);\n        } else if inner_height > 0 && idx >= self.tree_scroll as usize + inner_height {\n            self.tree_scroll =\n                u16::try_from(idx.saturating_sub(inner_height.saturating_sub(1))).unwrap_or(0);\n        }\n    }\n\n    pub(crate) fn handle_feature_click(\n        &mut self,\n        layer: usize,\n        feat: usize,\n        part: Option<usize>,\n        tree_height: u16,\n    ) {\n        match self.selected_item().clone() {\n            TreeItem::All => self.select_and_scroll(layer, None, None, tree_height),\n            TreeItem::Layer(_) => {\n                self.select_and_scroll(layer, Some(feat), None, tree_height);\n            }\n            _ => self.select_and_scroll(layer, Some(feat), part, tree_height),\n        }\n    }\n}\n\nfn error_size(row: &LsRow) -> usize {\n    match row {\n        LsRow::Error { size: Some(s), .. } => *s,\n        _ => 0,\n    }\n}\n\nfn file_cmp(a: &LsRow, b: &LsRow, col: FileSortColumn, asc: bool) -> std::cmp::Ordering {\n    use std::cmp::Ordering;\n    let ord = match (a, b) {\n        (LsRow::Info { info: ai, .. }, LsRow::Info { info: bi, .. }) => match col {\n            FileSortColumn::File => ai.path.cmp(&bi.path),\n            FileSortColumn::Size => ai.size.cmp(&bi.size),\n            FileSortColumn::EncPct => match (ai.encoding_pct, bi.encoding_pct) {\n                (Some(a), Some(b)) => a.total_cmp(&b),\n                (None, None) => Ordering::Equal,\n                (None, Some(_)) => Ordering::Greater,\n                (Some(_), None) => Ordering::Less,\n            },\n            FileSortColumn::Layers => ai.layers.cmp(&bi.layers),\n            FileSortColumn::Features => ai.features.cmp(&bi.features),\n        },\n        (LsRow::Info { .. }, _) => Ordering::Less,\n        (_, LsRow::Info { .. }) => Ordering::Greater,\n        (LsRow::Error { .. }, LsRow::Error { .. }) => match col {\n            FileSortColumn::Size => error_size(a).cmp(&error_size(b)),\n            _ => a.path().cmp(b.path()),\n        },\n        _ => a.path().cmp(b.path()),\n    };\n    if asc { ord } else { ord.reverse() }\n}\n\nfn poly_verts(poly: &Polygon<i32>) -> Vec<[f64; 2]> {\n    poly.exterior()\n        .0\n        .iter()\n        .copied()\n        .chain(poly.interiors().iter().flat_map(|r| r.0.iter().copied()))\n        .map(coord_f64)\n        .collect()\n}\n\nfn geometry_vertices(geom: &Geometry<i32>, part: Option<usize>) -> Vec<[f64; 2]> {\n    match (geom, part) {\n        (Geometry::<i32>::Point(p), None) => vec![coord_f64(p.0)],\n        (Geometry::<i32>::LineString(ls), None) => ls.0.iter().copied().map(coord_f64).collect(),\n        (Geometry::<i32>::MultiPoint(mp), None) => mp.iter().map(|p| coord_f64(p.0)).collect(),\n        (Geometry::<i32>::Polygon(poly), None) => poly_verts(poly),\n        (Geometry::<i32>::MultiLineString(mls), None) => mls\n            .iter()\n            .flat_map(|ls| ls.0.iter().copied().map(coord_f64))\n            .collect(),\n        (Geometry::<i32>::MultiPolygon(mpoly), None) => mpoly.iter().flat_map(poly_verts).collect(),\n        (Geometry::<i32>::MultiPoint(mp), Some(p)) => {\n            mp.0.get(p)\n                .map(|pt| vec![coord_f64(pt.0)])\n                .unwrap_or_default()\n        }\n        (Geometry::<i32>::MultiLineString(mls), Some(p)) => mls\n            .0\n            .get(p)\n            .map(|ls| ls.0.iter().copied().map(coord_f64).collect())\n            .unwrap_or_default(),\n        (Geometry::<i32>::MultiPolygon(mpoly), Some(p)) => {\n            mpoly.0.get(p).map(poly_verts).unwrap_or_default()\n        }\n        _ => Vec::new(),\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/.gitignore",
    "content": "/target\n"
  },
  {
    "path": "rust/mlt-core/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.9.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.8.0...rust-mlt-core-v0.9.0) - 2026-04-29\n\n### Added\n\n- *(rust)* add secondary Hilbert dictionary sorting on select tiles ([#1349](https://github.com/maplibre/maplibre-tile-spec/pull/1349))\n\n### Fixed\n\n- *(rust)* terrible fsst compression ratio due to reusing compressor results for unrelated shared dicts ([#1333](https://github.com/maplibre/maplibre-tile-spec/pull/1333))\n\n### Other\n\n- *(rust)* presence detection ([#1359](https://github.com/maplibre/maplibre-tile-spec/pull/1359))\n- *(rust)* simplify encoding ([#1358](https://github.com/maplibre/maplibre-tile-spec/pull/1358))\n- *(rust)* refactor low level encoding ([#1355](https://github.com/maplibre/maplibre-tile-spec/pull/1355))\n- *(rust)* consolidate encoders with common buffers ([#1352](https://github.com/maplibre/maplibre-tile-spec/pull/1352))\n- *(rust)* compute more stats before sort to enable deduplicating presence in v2 ([#1348](https://github.com/maplibre/maplibre-tile-spec/pull/1348))\n- *(rust)* simplify StagingId ([#1347](https://github.com/maplibre/maplibre-tile-spec/pull/1347))\n- *(rust)* new encoding methods ([#1346](https://github.com/maplibre/maplibre-tile-spec/pull/1346))\n- *(rust)* rm \"01\" from TileLayer01, StagedLayer01 ([#1345](https://github.com/maplibre/maplibre-tile-spec/pull/1345))\n- *(rust)* remove unused staging code and eq impl ([#1344](https://github.com/maplibre/maplibre-tile-spec/pull/1344))\n- *(rust)* follow up to Presence cleanup ([#1337](https://github.com/maplibre/maplibre-tile-spec/pull/1337))\n- *(rust)* simplify debug formatting ([#1336](https://github.com/maplibre/maplibre-tile-spec/pull/1336))\n- *(rust)* IdValues→ParsedId+StagedId, simplify presence ([#1334](https://github.com/maplibre/maplibre-tile-spec/pull/1334))\n- *(rust)* push fsst training up one level to not gain false statistics ([#1335](https://github.com/maplibre/maplibre-tile-spec/pull/1335))\n- *(rust)* rework presence to use bitvec ([#1329](https://github.com/maplibre/maplibre-tile-spec/pull/1329))\n\n## [0.8.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.7.0...rust-mlt-core-v0.8.0) - 2026-04-18\n\n### Fixed\n\n- fix(rust) from_tile tessellates when appropriate ([#1314](https://github.com/maplibre/maplibre-tile-spec/pull/1314))\n- *(rust)* geo builds on wasm, remove unnecessary feature gates ([#1297](https://github.com/maplibre/maplibre-tile-spec/pull/1297))\n\n### Other\n\n- *(rust)* cleanup Morton structs ([#1310](https://github.com/maplibre/maplibre-tile-spec/pull/1310))\n- *(rust)* cleanup dict ranges ([#1313](https://github.com/maplibre/maplibre-tile-spec/pull/1313))\n- remove ALP everywhere - it was never implemented ([#1128](https://github.com/maplibre/maplibre-tile-spec/pull/1128))\n- *(rust)* use Coord32 instead of tuple ([#1312](https://github.com/maplibre/maplibre-tile-spec/pull/1312))\n- *(rust)* rm RawStreamData and EncodedStreamData ([#1309](https://github.com/maplibre/maplibre-tile-spec/pull/1309))\n- *(rust)* Coord32 cleanup, dep update ([#1304](https://github.com/maplibre/maplibre-tile-spec/pull/1304))\n- *(rust)* update geo, simplify tessellation ([#1305](https://github.com/maplibre/maplibre-tile-spec/pull/1305))\n- Add offline docs.rs-style workspace docs check to Rust CI and fix surfaced rustdoc links ([#1295](https://github.com/maplibre/maplibre-tile-spec/pull/1295))\n- docs(rust) Fix broken doc links ([#1293](https://github.com/maplibre/maplibre-tile-spec/pull/1293))\n\n## [0.7.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.6.0...rust-mlt-core-v0.7.0) - 2026-04-13\n\n### Added\n\n- *(rust)* trigram based shared dictionary selection ([#1283](https://github.com/maplibre/maplibre-tile-spec/pull/1283))\n- *(rust)* allow delta-rle picking in stream selection ([#1258](https://github.com/maplibre/maplibre-tile-spec/pull/1258))\n- *(rust)* impl memory budget reset ([#1257](https://github.com/maplibre/maplibre-tile-spec/pull/1257))\n- *(rust)* enable FPF for id encoding ([#1243](https://github.com/maplibre/maplibre-tile-spec/pull/1243))\n- *(rust)* mlt convert, encoder ([#1240](https://github.com/maplibre/maplibre-tile-spec/pull/1240))\n- *(rust)* enhance property iteration ([#1225](https://github.com/maplibre/maplibre-tile-spec/pull/1225))\n- *(rust)* SharedDict/Plain string enc support ([#1213](https://github.com/maplibre/maplibre-tile-spec/pull/1213))\n\n### Fixed\n\n- don't unwrap in `adjust_alloc` ([#1275](https://github.com/maplibre/maplibre-tile-spec/pull/1275))\n- *(rust)* overflow-panic found by the fuzzer ([#1268](https://github.com/maplibre/maplibre-tile-spec/pull/1268))\n- *(rust)* fix geometry decoding fuzz bug ([#1264](https://github.com/maplibre/maplibre-tile-spec/pull/1264))\n- *(rust)* fix decoding panic found by fuzz ([#1261](https://github.com/maplibre/maplibre-tile-spec/pull/1261))\n- *(rust)* speed up encoding by 30% due to dup sort bug ([#1260](https://github.com/maplibre/maplibre-tile-spec/pull/1260))\n- *(rust)* disallow mem leaks in rust, fix test ([#1256](https://github.com/maplibre/maplibre-tile-spec/pull/1256))\n- *(rust)* test helper accidentally exposed ([#1250](https://github.com/maplibre/maplibre-tile-spec/pull/1250))\n\n### Other\n\n- Use `impl Iterator` in more places ([#1288](https://github.com/maplibre/maplibre-tile-spec/pull/1288))\n- *(rust)* remove a few bad performance choices (+6% encoder gain) ([#1287](https://github.com/maplibre/maplibre-tile-spec/pull/1287))\n- *(rust)* resolve most of the encoding quality + performance benefits of java ([#1281](https://github.com/maplibre/maplibre-tile-spec/pull/1281))\n- *(rust)* Don't allocate for calculating the hilbert params and resulting parameters ([#1285](https://github.com/maplibre/maplibre-tile-spec/pull/1285))\n- *(rust)* improve fuzzer ([#1278](https://github.com/maplibre/maplibre-tile-spec/pull/1278))\n- *(rust)* make sure we benchmark ourselves against the compressed variants ([#1194](https://github.com/maplibre/maplibre-tile-spec/pull/1194))\n- *(rust)* make staging differ optional from non-opt columns ([#1276](https://github.com/maplibre/maplibre-tile-spec/pull/1276))\n- *(rust)* cache morton computation ([#1277](https://github.com/maplibre/maplibre-tile-spec/pull/1277))\n- benchmark and fix various performance issues in the encoder ([#1273](https://github.com/maplibre/maplibre-tile-spec/pull/1273))\n- fix weird unessary allocation ([#1272](https://github.com/maplibre/maplibre-tile-spec/pull/1272))\n- *(rust)* reuse buffers, casting ([#1267](https://github.com/maplibre/maplibre-tile-spec/pull/1267))\n- *(rust)* Hotpath based profiling ([#1269](https://github.com/maplibre/maplibre-tile-spec/pull/1269))\n- *(rust)* add StreamCtx to simplify callbacks ([#1263](https://github.com/maplibre/maplibre-tile-spec/pull/1263))\n- *(rust)* remove unused auto_u32/u64 ([#1265](https://github.com/maplibre/maplibre-tile-spec/pull/1265))\n- *(rust)* rm obsolete encode_* funcs ([#1262](https://github.com/maplibre/maplibre-tile-spec/pull/1262))\n- *(rust)* massive rewrite of the encoder ([#1254](https://github.com/maplibre/maplibre-tile-spec/pull/1254))\n- *(rust)* re-enable the fuzzer in CI ([#1244](https://github.com/maplibre/maplibre-tile-spec/pull/1244))\n- *(rust)* improve decoding benchmarks ([#1247](https://github.com/maplibre/maplibre-tile-spec/pull/1247))\n- *(rust)* clean up utils ([#1251](https://github.com/maplibre/maplibre-tile-spec/pull/1251))\n- *(rust)* consolidate utils ([#1248](https://github.com/maplibre/maplibre-tile-spec/pull/1248))\n- *(rust)* move frames/v01 -> decoder, adj use ([#1246](https://github.com/maplibre/maplibre-tile-spec/pull/1246))\n- *(rust)* move more code to encoder ([#1245](https://github.com/maplibre/maplibre-tile-spec/pull/1245))\n- *(rust)* move all encoding code to separate dir ([#1241](https://github.com/maplibre/maplibre-tile-spec/pull/1241))\n- *(rust)* streamline StagedStrings and StagedSharedDict ([#1236](https://github.com/maplibre/maplibre-tile-spec/pull/1236))\n- *(rust)* add tests with no presence streams ([#1233](https://github.com/maplibre/maplibre-tile-spec/pull/1233))\n- *(rust)* encoder rework, rm profiles ([#1232](https://github.com/maplibre/maplibre-tile-spec/pull/1232))\n- *(rust)* mv tessellation to core ([#1220](https://github.com/maplibre/maplibre-tile-spec/pull/1220))\n- *(rust)* improve synthetics, geojson ([#1210](https://github.com/maplibre/maplibre-tile-spec/pull/1210))\n- add large FastPFOR synthetics ([#1205](https://github.com/maplibre/maplibre-tile-spec/pull/1205))\n- *(rust)* use -ize spelling ([#1200](https://github.com/maplibre/maplibre-tile-spec/pull/1200))\n- *(rust)* implement feature/property iterator and more type state ([#1198](https://github.com/maplibre/maplibre-tile-spec/pull/1198))\n- *(rust)* type state to represent fully-decoded layers ([#1171](https://github.com/maplibre/maplibre-tile-spec/pull/1171))\n- *(rust)* cleanup Results, minor styling ([#1192](https://github.com/maplibre/maplibre-tile-spec/pull/1192))\n\n## [0.6.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.5.0...rust-mlt-core-v0.6.0) - 2026-03-23\n\n### Other\n\n- *(rust)* migrate to Rust fastpfor ([#1190](https://github.com/maplibre/maplibre-tile-spec/pull/1190))\n\n## [0.5.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.4.0...rust-mlt-core-v0.5.0) - 2026-03-17\n\n### Added\n\n- *(rust)* allow resorting in the optimiser ([#1133](https://github.com/maplibre/maplibre-tile-spec/pull/1133))\n- *(rust)* migrate the property optimiser to the PGO architecture ([#1118](https://github.com/maplibre/maplibre-tile-spec/pull/1118))\n- *(rust)* migrate geometrys to allow for PGO ([#1112](https://github.com/maplibre/maplibre-tile-spec/pull/1112))\n- *(rust)* PGO based Id optimiser ([#1105](https://github.com/maplibre/maplibre-tile-spec/pull/1105))\n\n### Other\n\n- *(rust)* memory budgeting, codecs  ([#1168](https://github.com/maplibre/maplibre-tile-spec/pull/1168))\n- *(rust)* minor noop cleanup ([#1167](https://github.com/maplibre/maplibre-tile-spec/pull/1167))\n- *(rust)* introduce EncDec decode states ([#1166](https://github.com/maplibre/maplibre-tile-spec/pull/1166))\n- *(rust)* rename EncDec variants ([#1165](https://github.com/maplibre/maplibre-tile-spec/pull/1165))\n- *(rust)* add stateful decoder ([#1163](https://github.com/maplibre/maplibre-tile-spec/pull/1163))\n- *(rust)* rm TryFrom EncodedGeometry,EncodedId ([#1162](https://github.com/maplibre/maplibre-tile-spec/pull/1162))\n- *(rust)* rm FromDecoded, use encode(...) ([#1161](https://github.com/maplibre/maplibre-tile-spec/pull/1161))\n- *(rust)* finish big refactoring ([#1160](https://github.com/maplibre/maplibre-tile-spec/pull/1160))\n- *(rust)* rename to IdValues and GeometryValues ([#1159](https://github.com/maplibre/maplibre-tile-spec/pull/1159))\n- *(rust)* mv impls out of models, use full wire round-trips ([#1158](https://github.com/maplibre/maplibre-tile-spec/pull/1158))\n- *(chore)* remove into_static, test fixes ([#1154](https://github.com/maplibre/maplibre-tile-spec/pull/1154))\n- *(rust)* rework WASM code to use TileLayer ([#1153](https://github.com/maplibre/maplibre-tile-spec/pull/1153))\n- *(rust)* remove unnecessary to_owned calls ([#1151](https://github.com/maplibre/maplibre-tile-spec/pull/1151))\n- *(rust)* introduce staging types in Rust layer implementation ([#1149](https://github.com/maplibre/maplibre-tile-spec/pull/1149))\n- *(rust)* introduce staging types ([#1148](https://github.com/maplibre/maplibre-tile-spec/pull/1148))\n- *(rust)* code structure and remove NameRef ([#1147](https://github.com/maplibre/maplibre-tile-spec/pull/1147))\n- *(rust)* refactor parsing and encoding code ([#1144](https://github.com/maplibre/maplibre-tile-spec/pull/1144))\n- *(rust)* get rid of borrowme, add EncDec enum ([#1141](https://github.com/maplibre/maplibre-tile-spec/pull/1141))\n- *(rust)* simplify ID model ([#1139](https://github.com/maplibre/maplibre-tile-spec/pull/1139))\n- *(rust)* replace FromEncoded with Decode and decode_into ([#1134](https://github.com/maplibre/maplibre-tile-spec/pull/1134))\n- *(rust)* move impls out of model, scalars ([#1132](https://github.com/maplibre/maplibre-tile-spec/pull/1132))\n- *(rust)* rm borrowme from Layer01 ([#1129](https://github.com/maplibre/maplibre-tile-spec/pull/1129))\n- *(rust)* remove unused defaults ([#1127](https://github.com/maplibre/maplibre-tile-spec/pull/1127))\n- *(rust)* use enum_dispatch where appropriate, Cow for name ([#1125](https://github.com/maplibre/maplibre-tile-spec/pull/1125))\n- *(rust)* streamline str decoding ([#1121](https://github.com/maplibre/maplibre-tile-spec/pull/1121))\n- *(rust)* string performance benchmark ([#1123](https://github.com/maplibre/maplibre-tile-spec/pull/1123))\n- refactor!(rust): make `FromEncoded` and `FromDecoded` `pub(crate)` ([#1119](https://github.com/maplibre/maplibre-tile-spec/pull/1119))\n- *(rust)* use strum::IntoStaticStr ([#1122](https://github.com/maplibre/maplibre-tile-spec/pull/1122))\n- *(rust)* simplify Morton code decoding with SIMD ([#1114](https://github.com/maplibre/maplibre-tile-spec/pull/1114))\n- *(rust)* cleanup after big move ([#1113](https://github.com/maplibre/maplibre-tile-spec/pull/1113))\n- *(rust)* clean up all module and mod files ([#1111](https://github.com/maplibre/maplibre-tile-spec/pull/1111))\n- *(rust)* rename mod layer to frames ([#1110](https://github.com/maplibre/maplibre-tile-spec/pull/1110))\n- *(rust)* rm PropValue, consistent Cows ([#1109](https://github.com/maplibre/maplibre-tile-spec/pull/1109))\n- *(rust)* move name into DecodedStrings ([#1108](https://github.com/maplibre/maplibre-tile-spec/pull/1108))\n- *(rust)* change tests to be based on the new trait based optimization api ([#1104](https://github.com/maplibre/maplibre-tile-spec/pull/1104))\n\n## [0.4.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.3.0...rust-mlt-core-v0.4.0) - 2026-03-10\n\n### Added\n\n- plumbing for profile optimization ([#1101](https://github.com/maplibre/maplibre-tile-spec/pull/1101))\n- *(rust)* trait based automatic optimiser ([#1093](https://github.com/maplibre/maplibre-tile-spec/pull/1093))\n\n### Fixed\n\n- *(rust)* migrate part of our lengths from usize to u32 ([#1078](https://github.com/maplibre/maplibre-tile-spec/pull/1078))\n\n### Other\n\n- *(rust)* rework our internal data model ([#1099](https://github.com/maplibre/maplibre-tile-spec/pull/1099))\n- *(rust)* rename structs to model ([#1100](https://github.com/maplibre/maplibre-tile-spec/pull/1100))\n- *(rust)* handle u32->usize, int overflows ([#1097](https://github.com/maplibre/maplibre-tile-spec/pull/1097))\n- *(rust)* move all structs to structs.rs ([#1094](https://github.com/maplibre/maplibre-tile-spec/pull/1094))\n- renamings in the encoder ([#1090](https://github.com/maplibre/maplibre-tile-spec/pull/1090))\n- *(rust)* refactoring step 3 ([#1088](https://github.com/maplibre/maplibre-tile-spec/pull/1088))\n- *(rust)* refactor name storage ([#1087](https://github.com/maplibre/maplibre-tile-spec/pull/1087))\n- *(rust)* more core renames, rm EncodedValues::typ ([#1084](https://github.com/maplibre/maplibre-tile-spec/pull/1084))\n- *(rust)* some more renames of internals ([#1082](https://github.com/maplibre/maplibre-tile-spec/pull/1082))\n- *(rust)* noop, only renames for future refactoring ([#1081](https://github.com/maplibre/maplibre-tile-spec/pull/1081))\n- make sure we write u32 varints instead of u64 varints ([#1080](https://github.com/maplibre/maplibre-tile-spec/pull/1080))\n- use simd for morton decoding ([#1069](https://github.com/maplibre/maplibre-tile-spec/pull/1069))\n\n## [0.3.0](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-core-v0.2.0...rust-mlt-core-v0.3.0) - 2026-03-08\n\n### Added\n\n- *(rust)* property encoder optimizer ([#1042](https://github.com/maplibre/maplibre-tile-spec/pull/1042))\n- *(rust)* Id optimizer impl ([#1043](https://github.com/maplibre/maplibre-tile-spec/pull/1043))\n- *(rust)* Geometry optimizer ([#1045](https://github.com/maplibre/maplibre-tile-spec/pull/1045))\n\n### Other\n\n- *(rust)* rm Dict and FsstDict from SharedDict encoding ([#1068](https://github.com/maplibre/maplibre-tile-spec/pull/1068))\n- *(rust)* major rework of the shared dict ([#1066](https://github.com/maplibre/maplibre-tile-spec/pull/1066))\n- *(rust)* a bit more geotype cleanup ([#1059](https://github.com/maplibre/maplibre-tile-spec/pull/1059))\n- *(rust)* cleanup geotype code ([#1058](https://github.com/maplibre/maplibre-tile-spec/pull/1058))\n- *(rust)* move geotype code to its own mod ([#1053](https://github.com/maplibre/maplibre-tile-spec/pull/1053))\n- *(rust)* Fuzz the return path for roundtrip-ability ([#1044](https://github.com/maplibre/maplibre-tile-spec/pull/1044))\n- *(rust)* bump fastpfor, enable SIMD test, cleanup ([#1052](https://github.com/maplibre/maplibre-tile-spec/pull/1052))\n- move property tests to be based on the public API ([#1038](https://github.com/maplibre/maplibre-tile-spec/pull/1038))\n\n## [0.2.0](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-core-v0.1.2...rust-mlt-core-v0.2.0) - 2026-03-04\n\n### Added\n\n- *(rust)* enable configuring the shared dictionarys fully ([#1015](https://github.com/maplibre/maplibre-tile-spec/pull/1015))\n- *(rust)* shared dictionary encoding support ([#1006](https://github.com/maplibre/maplibre-tile-spec/pull/1006))\n- *(rust)* add rust morton encoding ([#1005](https://github.com/maplibre/maplibre-tile-spec/pull/1005))\n- *(rust)* implement `arbitrary::Arbitrary` for simpler fuzzing setups ([#991](https://github.com/maplibre/maplibre-tile-spec/pull/991))\n\n### Fixed\n\n- *(encoders)* F64 encoded as f64 ([#1009](https://github.com/maplibre/maplibre-tile-spec/pull/1009))\n- *(rust)* LineString and MultiLineString geometries ([#989](https://github.com/maplibre/maplibre-tile-spec/pull/989))\n- *(rust)* encoding of normalized mixed multi + regular geometrys panics ([#981](https://github.com/maplibre/maplibre-tile-spec/pull/981))\n\n### Other\n\n- *(rust)* rework string parsing ([#1026](https://github.com/maplibre/maplibre-tile-spec/pull/1026))\n- *(rust)* move optional into EncodedPropValue ([#1024](https://github.com/maplibre/maplibre-tile-spec/pull/1024))\n- *(rust)* prop strings mod ([#1023](https://github.com/maplibre/maplibre-tile-spec/pull/1023))\n- *(rust)* rename IntEncoder and add IntEncoding ([#1019](https://github.com/maplibre/maplibre-tile-spec/pull/1019))\n- *(rust)* move prop internals to \"big enum\" arch ([#1014](https://github.com/maplibre/maplibre-tile-spec/pull/1014))\n- clean up how FromDecoded interacts with strings ([#1011](https://github.com/maplibre/maplibre-tile-spec/pull/1011))\n- rename `Encoder` -> `PhysicalCodecs` ([#1010](https://github.com/maplibre/maplibre-tile-spec/pull/1010))\n- *(rust)* start fixing rust synthetics ([#1002](https://github.com/maplibre/maplibre-tile-spec/pull/1002))\n- *(synthetic)* add Morton fixture synthetic test ([#960](https://github.com/maplibre/maplibre-tile-spec/pull/960))\n- More testing around geometry combinations ([#982](https://github.com/maplibre/maplibre-tile-spec/pull/982))\n- *(rust)* remove AVX failing test ([#999](https://github.com/maplibre/maplibre-tile-spec/pull/999))\n- *(rust)* rm json5, inf floats to string ([#1000](https://github.com/maplibre/maplibre-tile-spec/pull/1000))\n- *(rust)* Add an encoding benchmark ([#997](https://github.com/maplibre/maplibre-tile-spec/pull/997))\n\n## [0.1.2](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-core-v0.1.1...mlt-core-v0.1.2) - 2026-02-25\n\n### Added\n\n- Release done to test if some of the release automation is working. Should fix `cargo-binstall mlt`\n\n## [0.1.1](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-core-v0.1.0...mlt-core-v0.1.1) - 2026-02-25\n\n### Added\n\n- *(rust)* Stream encoding optimizer ([#950](https://github.com/maplibre/maplibre-tile-spec/pull/950))\n- *(rust)* Synthetic generation ([#947](https://github.com/maplibre/maplibre-tile-spec/pull/947))\n- *(rust)* FastPFOR encoding support ([#936](https://github.com/maplibre/maplibre-tile-spec/pull/936))\n- *(rust)* implement geometry encoding functionality ([#933](https://github.com/maplibre/maplibre-tile-spec/pull/933))\n- *(rust)* string encoding support ([#937](https://github.com/maplibre/maplibre-tile-spec/pull/937))\n- *(rust)* Point Geometry encoding ([#932](https://github.com/maplibre/maplibre-tile-spec/pull/932))\n- *(rust)* Property encoding ([#929](https://github.com/maplibre/maplibre-tile-spec/pull/929))\n- *(rust)* impl stream encoding ([#924](https://github.com/maplibre/maplibre-tile-spec/pull/924))\n- *(rust)* impl `encode_componentwise_delta_vec2s` ([#930](https://github.com/maplibre/maplibre-tile-spec/pull/930))\n- *(rust)* Id encoder implementation ([#906](https://github.com/maplibre/maplibre-tile-spec/pull/906))\n\n### Fixed\n\n- *(rust)* simplify int parsing errors ([#921](https://github.com/maplibre/maplibre-tile-spec/pull/921))\n\n### Other\n\n- *(rust)* improve rust synthetics generation ([#951](https://github.com/maplibre/maplibre-tile-spec/pull/951))\n- *(rust)* Reword the docs ([#946](https://github.com/maplibre/maplibre-tile-spec/pull/946))\n- *(rust)* more renames ([#945](https://github.com/maplibre/maplibre-tile-spec/pull/945))\n- *(rust)* rename encoding->encoder\n- *(rust)* rename encoding-related types for clarity ([#944](https://github.com/maplibre/maplibre-tile-spec/pull/944))\n- *(rust)* optimize some UI code ([#939](https://github.com/maplibre/maplibre-tile-spec/pull/939))\n- *(rust)* simplify the proptest strategy ([#935](https://github.com/maplibre/maplibre-tile-spec/pull/935))\n- *(rust)* remove unused variables and simplify buffer handling ([#925](https://github.com/maplibre/maplibre-tile-spec/pull/925))\n- *(rust)* rename LogicalDecoder and PhysicalDecoder to codecs ([#923](https://github.com/maplibre/maplibre-tile-spec/pull/923))\n- *(rust)* improve error handling and messaging in error types ([#920](https://github.com/maplibre/maplibre-tile-spec/pull/920))\n- *(rust)* rename `UnsupportedLogicalTechniqueCombination` ([#919](https://github.com/maplibre/maplibre-tile-spec/pull/919))\n- *(rust)* Shift id encoding to the stream ([#918](https://github.com/maplibre/maplibre-tile-spec/pull/918))\n- *(rust)* add basic benchmark infrastructure ([#912](https://github.com/maplibre/maplibre-tile-spec/pull/912))\n- *(rust)* cleanup errors and some namespaces ([#917](https://github.com/maplibre/maplibre-tile-spec/pull/917))\n- *(rust)* cleanup around the property mod ([#915](https://github.com/maplibre/maplibre-tile-spec/pull/915))\n- *(rust)* more cleanups ([#914](https://github.com/maplibre/maplibre-tile-spec/pull/914))\n- *(rust)* rename raw -> encoded ([#910](https://github.com/maplibre/maplibre-tile-spec/pull/910))\n- *(rust)* refactor and add plumbing for encoding ([#909](https://github.com/maplibre/maplibre-tile-spec/pull/909))\n"
  },
  {
    "path": "rust/mlt-core/Cargo.toml",
    "content": "[package]\nname = \"mlt-core\"\ndescription = \"MapLibre Tile library code\"\nversion = \"0.9.0\"\nauthors.workspace = true\ncategories.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[[bench]]\nname = \"decoding_e2e\"\nharness = false\nrequired-features = [\"__private\"]\n\n[[bench]]\nname = \"encoding_e2e\"\nharness = false\nrequired-features = [\"__private\"]\n\n[[bench]]\nname = \"decoding_utils\"\nharness = false\nrequired-features = [\"__private\"]\n\n[[bench]]\nname = \"decoding_strings\"\nharness = false\nrequired-features = [\"__private\"]\n\n[[bench]]\nname = \"encoding_from_mvt\"\nharness = false\nrequired-features = [\"__private\"]\n\n[features]\ndefault = []\narbitrary = [\"dep:arbitrary\", \"geo-types/arbitrary\"]\n# Used internally for testing and benchmarking, not intended for public use\n__private = []\n\n[dependencies]\narbitrary = { workspace = true, optional = true }\nbitvec.workspace = true\nbytemuck.workspace = true\nderive-debug.workspace = true\nenum_dispatch.workspace = true\nfastpfor.workspace = true\nfsst-rs.workspace = true\ngeo = { workspace = true, features = [\"earcut\"] }\ngeo-types.workspace = true\nhex.workspace = true\nhilbert_2d.workspace = true\nhotpath.workspace = true\ninteger-encoding.workspace = true\nmvt-reader.workspace = true\nnum-traits.workspace = true\nnum_enum.workspace = true\nprobabilistic-collections.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nstrum.workspace = true\nthiserror.workspace = true\nunion-find.workspace = true\nwide.workspace = true\nzigzag.workspace = true\n\n[dev-dependencies]\nbrotli.workspace = true\ncriterion.workspace = true\nflate2.workspace = true\ngeo-types = { workspace = true, features = [\"arbitrary\"] }\ninsta.workspace = true\npretty_assertions.workspace = true\nproptest.workspace = true\nproptest-derive.workspace = true\nrstest.workspace = true\ntest_each_file.workspace = true\nzstd.workspace = true\n\n[lints]\nworkspace = true\n\n[[test]]\nname = \"geojson\"\nrequired-features = [\"__private\"]\n\n[[test]]\nname = \"snapshots\"\nrequired-features = [\"__private\"]\n"
  },
  {
    "path": "rust/mlt-core/README.md",
    "content": "# `MapLibre Tile` (MLT) Rust library\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-dark-bg.svg\">\n  <source media=\"(prefers-color-scheme: light)\" srcset=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg\">\n  <img alt=\"MapLibre Logo\" src=\"https://maplibre.org/img/maplibre-logos/maplibre-logo-for-light-bg.svg\" width=\"200\">\n</picture>\n\nThe `MapLibre Tile` specification is mainly inspired by the [Mapbox Vector Tile (MVT)](https://github.com/mapbox/vector-tile-spec) specification,\nbut has been redesigned from the ground up to address the challenges of rapidly growing geospatial data volumes\nand complex next-generation geospatial source formats as well as to leverage the capabilities of modern hardware and APIs.\nMLT is specifically designed for modern and next generation graphics APIs to enable high-performance processing and rendering of\nlarge (planet-scale) 2D and 2.5 basemaps.\n\nIn particular, MLT offers the following features:\n- **Improved compression ratio**:\n  Up to 6x on large encoded tiles, based on a column oriented layout with recursively applied (custom) lightweight encodings.\n  This leads to reduced latency, storage, and egress costs and, in particular, improved cache utilization.\n- **Better decoding performance**:\n  Fast lightweight encodings which can be used in combination with SIMD/vectorization instructions.\n- **Support for linear referencing and m-values**:\n  To efficiently support the upcoming next generation source formats such as Overture Maps (`GeoParquet`).\n- **Support for 3D coordinates**: i.e., elevation\n- **Support for complex types**: including nested properties, lists, and maps\n- **Improved processing performance**,\n  based on storage and in-memory formats that are specifically designed for modern GL APIs, allowing for efficient processing on both CPU and GPU.\n  The formats are designed to be loaded into GPU buffers with little or no additional processing.\n\n📝 For a more in-depth exploration of MLT have a look at the [following slides](https://github.com/mactrem/presentations/blob/main/FOSS4G_2024_Europe/FOSS4G_2024_Europe.pdf), watch\n[this talk](https://www.youtube.com/watch?v=YHcoAFcsES0) or read [this paper](https://dl.acm.org/doi/10.1145/3748636.3763208) by MLT inventor Markus Tremmel.\n\n## Tile Structure\n\nTop level structure of a tile is a sequence of layers, where each layer consists of `(size, tag, data)` tuples:\n- `size: varint` - size of the data block in bytes, including the size of the `tag` field\n- `tag: varint` - identifies the block type, e.g. `0x01 = feature table v1`, `0x02 = raster layer`, `0x03 = routing table`, etc. We only define `0x01` for now.\n- `data: u8[]` - the actual data block of the specified size\n\nThis approach allows us to easily extend the format in the future by adding new block types, while keeping backward compatibility.\nParsers can skip unknown block types by reading the `size` and moving forward accordingly.\nFor now, we only define `0x01` for vector layers, and possibly a few more if needed.\n\nNote the ordering:\n`tag` is after the `size` because it is possible to treat it as a single byte for now, until the parser supports more than 127 types.\nThis allows the parser to efficiently skip unknown types without doing more expensive varint parsing.\n\n## Layer 0x01 - MVT compatibility\n\nStructure of the data if the `tag` above is 0x01.\nWe should focus this tag on MVT compatibility, offering exactly what we had in MVT, but allowing for a clearly defined set of encodings and other optimizations like tessellation.\nNo new data formats (per vertex data, nested data, 3d geometries, etc.).\nNo extendable encodings - once finalized, 0x01 will only allow what has been specified.\nThis will ensure that if a decoder declares \"0x01\" support, it will parse every specification-compliant 0x01 layer.\nFor any new features and encodings we will simply use a new tag ID, likely reusing most of the existing encoding/decoding code.\n\n- `name: string` - Name of the layer\n- `columnCount: varint` - Number of columns in the layer\n- each column is defined as:\n    - `columnType: varint` - same idea as `tag` above, e.g. `1 = id`, `2 = geometry`, `3 = int property`, etc.\n    - TODO...\n\nSee [`CONTRIBUTING.md`](../CONTRIBUTING.md) for additional pipeline docs.\n\n## Data Pipeline\n\nThe diagram below shows the full lifecycle of tile data — from raw bytes on the wire,\nthrough lazy parsing and optional per-column decoding, to zero-copy iteration or\nfully owned row-form access, and back to encoded bytes via the columnar encode pipeline.\n\n```mermaid\nflowchart TB\n    A([\"&[u8]  —  raw tile bytes\"])\n\n    subgraph DEC [\"Decoding\"]\n        B[\"<b>Parser::parse_layers(&[u8])</b>\n        <i>Create zero-copy views into input bytes\n        for each layer with almost no memory allocations</i>\"]\n\n        C[\"Layer&lt;Lazy>\n           Tag01(Layer01&lt;Lazy>) | Unknown\n           columns = LazyParsed::Raw(RawStream)\n           zero allocations for column data\"]\n\n        D[\"decode_all()  — or per-column:\n           decode_id() / decode_geometry() / decode_properties()\n\n           RawStream\n           → physical codec: FastPFor · varint · byte-RLE\n           → logical  codec: delta · zigzag · Morton · Hilbert\n           → typed column buffers Vec&lt;T>\"]\n\n        E[\"ParsedLayer  =  Layer&lt;Parsed>\n           all columns decoded into typed buffers\"]\n    end\n\n    subgraph ACCESS [\"Iterate  (zero-copy borrow)\"]\n        F[\"iter_features()  →  Layer01FeatureIter\"]\n        G[\"FeatureRef\n           id: Option&lt;u64>\n           geometry reference\n           property iterator\"]\n    end\n\n    H([\"Layer01::into_tile()\"])\n\n    subgraph BRIDGE [\"TileLayer  (row-oriented, fully owned)\"]\n        I[\"Vec&lt;TileFeature>\n           id:       Option&lt;u64>\n           geometry: geo_types::Geometry&lt;i32>\n           props:    Vec&lt;PropValue>\"]\n    end\n\n    subgraph ENC [\"Encoding\"]\n        J[\"StagedLayer::from(TileLayer)\n           owned columnar form\n           IdValues · GeometryValues · Vec&lt;StagedProperty>\"]\n\n        K[\"StagedLayer::encode()\n           / encode_auto() / encode_with_profile()\n           per-column compression applied\"]\n\n        L[\"EncodedLayer01  (wire-ready)\n           EncodedId · EncodedGeometry\n           Vec&lt;EncodedProperty>  each = Vec&lt;EncodedStream>\"]\n\n        M[\"EncodedLayer::write_to()\n           serialises: varint(size) + tag byte + column payloads\"]\n    end\n\n    N([\"Vec&lt;u8>  —  encoded tile bytes\"])\n\n    A --> B --> C --> D --> E\n    E -->|\"borrow\"| F --> G\n    E -->|\"own all data\"| H --> I\n    I -->|\"From&lt;TileLayer>\"| J --> K --> L --> M --> N\n\n    classDef io fill:#1e5c3a,color:#e8f5e9,stroke:#0a3d22\n    classDef bridge fill:#4a1c6b,color:#f3e5f5,stroke:#2d0b45\n    classDef dec fill:#1a3a5c,color:#e3f2fd,stroke:#0d1f35\n    classDef enc fill:#5c2a1a,color:#fbe9e7,stroke:#3d1510\n    class A,N io\n    class H,I bridge\n    class B,C,D,E,F,G dec\n    class J,K,L,M enc\n```\n\n**Key types and their roles:**\n\n| Type | Role |\n|------|------|\n| `Layer<Lazy>` / `Layer01<Lazy>` | Parsed frame with column byte slices still unprocessed; zero allocation beyond the parse pass |\n| `LazyParsed<Raw, Parsed>` | Type-state wrapper: `Raw(RawStream)` before decoding, `Parsed(T)` after, `ParsingFailed` on error |\n| `ParsedLayer` = `Layer<Parsed>` | All columns decoded; borrow-based iteration via `iter_features()` |\n| `TileLayer` | Row-oriented, fully owned bridge between decode and encode |\n| `StagedLayer` | Owned columnar data ready for compression/encoding |\n| `EncodedLayer01` | Wire-ready columnar data; written by `write_to()` with size + tag prefix |\n\n## Tools\n\nSee the `mlt` tool for various ways to interact with the parser and decoder.\nThis includes a terminal-based visualizer for exploring MLT files.\n"
  },
  {
    "path": "rust/mlt-core/benches/bench_utils.rs",
    "content": "#![allow(dead_code)]\n\nuse std::fs;\nuse std::path::Path;\n\n// This code runs in CI because of --all-targets, so make it run really fast.\n#[cfg(debug_assertions)]\npub const BENCHMARKED_ZOOM_LEVELS: [u8; 1] = [0];\n#[cfg(not(debug_assertions))]\npub const BENCHMARKED_ZOOM_LEVELS: [u8; 3] = [4, 7, 13];\n\n/// Recursively walk `dir` and collect all files with the given `extension`.\nfn walk_dir(dir: &Path, extension: &str, out: &mut Vec<(String, Vec<u8>)>) {\n    let entries =\n        fs::read_dir(dir).unwrap_or_else(|err| panic!(\"can't read {}: {err}\", dir.display()));\n    for entry in entries {\n        let entry =\n            entry.unwrap_or_else(|err| panic!(\"can't read entry in {}: {err}\", dir.display()));\n        let path = entry.path();\n        if path.is_dir() {\n            walk_dir(&path, extension, out);\n        } else {\n            let name = path.to_string_lossy();\n            if name.ends_with(extension) {\n                let data = fs::read(&path)\n                    .unwrap_or_else(|err| panic!(\"can't read {}: {err}\", path.display()));\n                out.push((path.to_string_lossy().into_owned(), data));\n            }\n        }\n    }\n}\n\n/// Load all `.mvt` files found recursively under `../../test`.\n///\n/// Returns `(path_string, raw_bytes)` pairs sorted by path.\n/// In debug builds (CI), returns only the first file to keep tests fast.\n#[must_use]\npub fn load_all_mvt_bytes() -> Vec<(String, Vec<u8>)> {\n    let dir = Path::new(env!(\"CARGO_MANIFEST_DIR\")).join(\"../../test\");\n    let mut tiles = Vec::new();\n    walk_dir(&dir, \".mvt\", &mut tiles);\n    assert!(\n        !tiles.is_empty(),\n        \"No .mvt files found under {}\",\n        dir.display()\n    );\n    tiles.sort_by(|a, b| a.0.cmp(&b.0));\n    #[cfg(debug_assertions)]\n    tiles.truncate(1);\n    tiles\n}\n\n#[must_use]\npub fn load_mlt_tiles(zoom: u8) -> Vec<(String, Vec<u8>)> {\n    load_tiles(zoom, \"expected/tag0x01/omt\", \".mlt\")\n}\n\n#[must_use]\npub fn load_tiles(zoom: u8, test_subpath: &str, extension: &str) -> Vec<(String, Vec<u8>)> {\n    let dir = Path::new(env!(\"CARGO_MANIFEST_DIR\"))\n        .join(\"../../test\")\n        .join(test_subpath);\n    let prefix = format!(\"{zoom}_\");\n    let mut tiles = Vec::new();\n    let entries =\n        fs::read_dir(&dir).unwrap_or_else(|err| panic!(\"can't read {}: {err}\", dir.display()));\n    for entry in entries {\n        let entry = entry.unwrap_or_else(|err| panic!(\"can't read entry {}: {err}\", dir.display()));\n        let file_name = entry.file_name();\n        let name = file_name.to_string_lossy();\n        if name.starts_with(&prefix)\n            && let Some(stem) = name.strip_suffix(extension)\n        {\n            let data = fs::read(entry.path())\n                .unwrap_or_else(|err| panic!(\"can't read {}: {err}\", entry.path().display()));\n            tiles.push((stem.to_string(), data));\n        }\n    }\n    assert!(\n        !tiles.is_empty(),\n        \"No tiles found for zoom level {zoom} in {}\",\n        dir.display()\n    );\n    tiles.sort_by(|a, b| a.0.cmp(&b.0));\n    tiles\n}\n\n#[must_use]\npub fn total_bytes(tiles: &[(String, Vec<u8>)]) -> usize {\n    tiles.iter().map(|(_, d)| d.len()).sum()\n}\n"
  },
  {
    "path": "rust/mlt-core/benches/decoding_e2e.rs",
    "content": "use std::hint::black_box;\nuse std::io::{Read as _, Write as _};\n\nuse criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};\nuse mlt_core::__private::{dec, parser};\n\n#[path = \"bench_utils.rs\"]\nmod bench_utils;\nuse bench_utils::{BENCHMARKED_ZOOM_LEVELS, load_mlt_tiles, load_tiles, total_bytes};\n\nfn load_proto_tiles(zoom: u8) -> Vec<(String, Vec<u8>)> {\n    load_tiles(zoom, \"fixtures/omt\", \".mvt\")\n}\n\nfn compress_gzip(data: &[u8]) -> Vec<u8> {\n    let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());\n    encoder.write_all(data).expect(\"gzip compress failed\");\n    encoder.finish().expect(\"gzip finish failed\")\n}\n\nfn decompress_gzip(data: &[u8]) -> Vec<u8> {\n    let mut decoder = flate2::read::GzDecoder::new(data);\n    let mut out = Vec::new();\n    decoder\n        .read_to_end(&mut out)\n        .expect(\"gzip decompress failed\");\n    out\n}\n\nfn compress_zstd(data: &[u8]) -> Vec<u8> {\n    zstd::encode_all(data, 3).expect(\"zstd compress failed\")\n}\n\nfn decompress_zstd(data: &[u8]) -> Vec<u8> {\n    zstd::decode_all(data).expect(\"zstd decompress failed\")\n}\n\nfn compress_brotli(data: &[u8]) -> Vec<u8> {\n    let mut out = Vec::new();\n    // quality 6, lgwin 22 (brotli defaults)\n    let mut encoder = brotli::CompressorWriter::new(&mut out, 4096, 6, 22);\n    encoder.write_all(data).expect(\"brotli compress failed\");\n    drop(encoder);\n    out\n}\n\nfn decompress_brotli(data: &[u8]) -> Vec<u8> {\n    let mut out = Vec::new();\n    let mut decoder = brotli::Decompressor::new(data, 4096);\n    decoder\n        .read_to_end(&mut out)\n        .expect(\"brotli decompress failed\");\n    out\n}\n\nfn compress_tiles(\n    tiles: &[(String, Vec<u8>)],\n    compress: fn(&[u8]) -> Vec<u8>,\n) -> Vec<(String, Vec<u8>)> {\n    tiles\n        .iter()\n        .map(|(name, data)| (name.clone(), compress(data)))\n        .collect()\n}\n\nfn mvt_parse(data: Vec<u8>) {\n    let reader = mvt_reader::Reader::new(black_box(data)).expect(\"mvt reader construction failed\");\n    let _ = black_box(reader);\n}\n\nfn mvt_decode(data: Vec<u8>) {\n    let reader = mvt_reader::Reader::new(black_box(data)).expect(\"mvt reader construction failed\");\n    let layers = reader\n        .get_layer_metadata()\n        .expect(\"mvt layer metadata failed\");\n    for layer in &layers {\n        let features = reader\n            .get_features(layer.layer_index)\n            .expect(\"mvt get_features failed\");\n        let _ = black_box(features);\n    }\n    let _ = black_box(reader);\n}\n\ntype Codec = (&'static str, fn(&[u8]) -> Vec<u8>, fn(&[u8]) -> Vec<u8>);\n\nfn identity(data: &[u8]) -> Vec<u8> {\n    data.to_vec()\n}\n\nconst CODECS: &[Codec] = &[\n    (\"none\", identity, identity),\n    (\"gzip\", compress_gzip, decompress_gzip),\n    (\"zstd\", compress_zstd, decompress_zstd),\n    (\"brotli\", compress_brotli, decompress_brotli),\n];\n\nfn bench_parse(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"parse\");\n\n    for zoom in BENCHMARKED_ZOOM_LEVELS {\n        let mlt_tiles = load_mlt_tiles(zoom);\n        let proto_tiles = load_proto_tiles(zoom);\n\n        // mlt parse\n        group.throughput(Throughput::Bytes(total_bytes(&mlt_tiles) as u64));\n        group.bench_with_input(BenchmarkId::new(\"mlt\", zoom), &mlt_tiles, |b, tiles| {\n            b.iter(|| {\n                for (_, data) in tiles {\n                    black_box(\n                        parser()\n                            .parse_layers(black_box(data))\n                            .expect(\"mlt parse failed\"),\n                    );\n                }\n            });\n        });\n\n        // mvt parse (per codec)\n        for &(codec_name, compress, decompress) in CODECS {\n            let compressed = compress_tiles(&proto_tiles, compress);\n            group.throughput(Throughput::Bytes(total_bytes(&compressed) as u64));\n            group.bench_with_input(\n                BenchmarkId::new(format!(\"mvt+{codec_name}\"), zoom),\n                &compressed,\n                |b, tiles| {\n                    b.iter_batched(\n                        || tiles.iter().map(|(_, d)| d.clone()).collect::<Vec<_>>(),\n                        |compressed_data| {\n                            for data in compressed_data {\n                                mvt_parse(decompress(black_box(&data)));\n                            }\n                        },\n                        BatchSize::LargeInput,\n                    );\n                },\n            );\n        }\n    }\n\n    group.finish();\n}\n\nfn bench_decode_all(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"decode_all\");\n\n    for zoom in BENCHMARKED_ZOOM_LEVELS {\n        let mlt_tiles = load_mlt_tiles(zoom);\n        let proto_tiles = load_proto_tiles(zoom);\n\n        // mlt decode_all\n        group.throughput(Throughput::Bytes(total_bytes(&mlt_tiles) as u64));\n        group.bench_with_input(BenchmarkId::new(\"mlt\", zoom), &mlt_tiles, |b, tiles| {\n            b.iter_batched(\n                || {\n                    tiles\n                        .iter()\n                        .map(|(_, v)| {\n                            parser()\n                                .parse_layers(black_box(v))\n                                .expect(\"mlt parse failed\")\n                        })\n                        .collect::<Vec<_>>()\n                },\n                |mlt| {\n                    let mut d = dec();\n                    let decoded: Vec<Vec<_>> = mlt\n                        .into_iter()\n                        .map(|layers| {\n                            d.reset_budget();\n                            let dec_tile = d.decode_all(layers).expect(\"mlt decode_all failed\");\n                            black_box(dec_tile)\n                        })\n                        .collect();\n                    black_box(decoded);\n                },\n                BatchSize::SmallInput,\n            );\n        });\n\n        // mvt decode_all (per codec)\n        for &(codec_name, compress, decompress) in CODECS {\n            let compressed = compress_tiles(&proto_tiles, compress);\n            group.throughput(Throughput::Bytes(total_bytes(&compressed) as u64));\n            group.bench_with_input(\n                BenchmarkId::new(format!(\"mvt+{codec_name}\"), zoom),\n                &compressed,\n                |b, tiles| {\n                    b.iter_batched(\n                        || tiles.iter().map(|(_, d)| d.clone()).collect::<Vec<_>>(),\n                        |compressed_data| {\n                            for data in compressed_data {\n                                mvt_decode(decompress(black_box(&data)));\n                            }\n                        },\n                        BatchSize::LargeInput,\n                    );\n                },\n            );\n        }\n    }\n\n    group.finish();\n}\n\ncriterion_group!(benches, bench_parse, bench_decode_all);\ncriterion_main!(benches);\n"
  },
  {
    "path": "rust/mlt-core/benches/decoding_strings.rs",
    "content": "use std::hint::black_box;\n\nuse criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};\nuse geo_types::Point;\nuse mlt_core::encoder::{\n    Codecs, Encoder, EncoderConfig, ExplicitEncoder, IntEncoder, LogicalEncoder, PhysicalEncoder,\n    Presence, StagedId, StagedLayer, StagedProperty, StagedSharedDict, StrEncoding,\n};\nuse mlt_core::test_helpers::{dec, parser};\nuse mlt_core::{GeometryValues, LendingIterator, ParsedLayer01, PropValueRef};\nuse strum::IntoEnumIterator as _;\n\n// This code runs in CI because of --all-targets, so make it run really fast.\n#[cfg(debug_assertions)]\npub const BENCHMARKED_LENGTHS: [usize; 1] = [1];\n#[cfg(not(debug_assertions))]\npub const BENCHMARKED_LENGTHS: [usize; 6] = [1, 20, 64, 256, 1024, 2048];\n\nfn limit<T>(values: impl Iterator<Item = T>) -> impl Iterator<Item = T> {\n    if cfg!(debug_assertions) {\n        values.take(1)\n    } else {\n        values.take(usize::MAX)\n    }\n}\n\n/// Generate a mix of strings with repetition so dictionary encoding has something to compress.\n/// The vocabulary is small relative to N, so values repeat often.\nfn make_strings(n: usize) -> Vec<String> {\n    const VOCAB: &[&str] = &[\n        \"highway\",\n        \"residential\",\n        \"motorway\",\n        \"primary\",\n        \"secondary\",\n        \"tertiary\",\n        \"water\",\n        \"forest\",\n        \"park\",\n        \"building\",\n        \"amenity\",\n        \"shop\",\n        \"landuse\",\n        \"natural\",\n        \"place\",\n        \"boundary\",\n    ];\n    black_box(\n        (0..n)\n            .map(|i| {\n                let idx = i % VOCAB.len();\n                if i.is_multiple_of(4) {\n                    VOCAB[idx].to_string()\n                } else {\n                    format!(\"{}_{}\", VOCAB[idx], i % 32)\n                }\n            })\n            .collect(),\n    )\n}\n\n/// Same pool as `make_strings`, but every 5th entry is `None` so the presence\n/// stream has real work to do.\nfn make_nullable_strings(n: usize) -> Vec<Option<String>> {\n    black_box(\n        make_strings(n)\n            .into_iter()\n            .enumerate()\n            .map(|(i, s)| if i.is_multiple_of(5) { None } else { Some(s) })\n            .collect(),\n    )\n}\n\n/// Build `n` degenerate point features at the origin for use as layer geometry.\nfn make_geometry(n: usize) -> GeometryValues {\n    let mut g = GeometryValues::default();\n    for _ in 0..n {\n        g.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n    }\n    g\n}\n\n/// Encode `props` into a single-layer tile with `n` point features and return wire bytes.\nfn encode_layer(n: usize, props: Vec<StagedProperty>, cfg: ExplicitEncoder) -> Vec<u8> {\n    let mut codecs = Codecs::default();\n    StagedLayer {\n        name: \"bench\".into(),\n        extent: 4096,\n        id: StagedId::None,\n        geometry: make_geometry(n),\n        properties: props,\n    }\n    .encode_into(\n        Encoder::with_explicit(EncoderConfig::default(), cfg),\n        &mut codecs,\n    )\n    .expect(\"encode_layer failed\")\n    .into_layer_bytes()\n    .expect(\"into_layer_bytes failed\")\n}\n\n/// Sum the byte lengths of all non-null string property values across all features.\n///\n/// Used as the benchmark measurement: the return value prevents the compiler from\n/// optimizing away the iteration, and its magnitude is proportional to work done.\nfn sum_str_lens(parsed: &ParsedLayer01<'_>) -> usize {\n    let mut total = 0;\n    let mut iter = parsed.iter_features();\n    while let Some(feat_res) = iter.next() {\n        total += feat_res\n            .unwrap()\n            .iter_all_properties()\n            .map(|v| {\n                if let Some(PropValueRef::Str(s)) = v {\n                    s.len()\n                } else {\n                    0\n                }\n            })\n            .sum::<usize>();\n    }\n    total\n}\n\n/// plain strings: vary the `IntEncoder` used for the length stream\nfn bench_plain_length_encoding(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"strings/plain/length_enc\");\n\n    for n in BENCHMARKED_LENGTHS {\n        let col = make_strings(n);\n        group.throughput(Throughput::Elements(n as u64));\n\n        for logical in limit(LogicalEncoder::iter()) {\n            for physical in limit(PhysicalEncoder::iter()) {\n                let int_enc = IntEncoder::new(logical, physical);\n                let bytes = encode_layer(\n                    n,\n                    vec![StagedProperty::str(\"name\", col.clone())],\n                    ExplicitEncoder::all(int_enc),\n                );\n\n                group.bench_with_input(\n                    BenchmarkId::new(format!(\"{logical:?}-{physical:?}\"), n),\n                    &bytes,\n                    |b, bytes| {\n                        b.iter(|| {\n                            let layer = parser()\n                                .parse_layers(bytes)\n                                .expect(\"parse\")\n                                .remove(0)\n                                .into_layer01()\n                                .expect(\"layer01\");\n                            let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                            black_box(sum_str_lens(&parsed))\n                        });\n                    },\n                );\n            }\n        }\n    }\n\n    group.finish();\n}\n\n/// FSST strings: vary the `IntEncoder` used for the symbol-length and value-length streams\nfn bench_fsst_length_encoding(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"strings/fsst/length_enc\");\n\n    for n in BENCHMARKED_LENGTHS {\n        let col = make_strings(n);\n        group.throughput(Throughput::Elements(n as u64));\n\n        for logical in limit(LogicalEncoder::iter()) {\n            for physical in limit(PhysicalEncoder::iter()) {\n                let int_enc = IntEncoder::new(logical, physical);\n                let bytes = encode_layer(\n                    n,\n                    vec![StagedProperty::str(\"name\", col.clone())],\n                    ExplicitEncoder::all_with_str(int_enc, StrEncoding::Fsst),\n                );\n\n                group.bench_with_input(\n                    BenchmarkId::new(format!(\"{logical:?}-{physical:?}\"), n),\n                    &bytes,\n                    |b, bytes| {\n                        b.iter(|| {\n                            let layer = parser()\n                                .parse_layers(bytes)\n                                .expect(\"parse\")\n                                .remove(0)\n                                .into_layer01()\n                                .expect(\"layer01\");\n                            let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                            black_box(sum_str_lens(&parsed))\n                        });\n                    },\n                );\n            }\n        }\n    }\n\n    group.finish();\n}\n\n/// Benchmark 3 – encoding type: plain vs FSST, fixed `IntEncoder`\nfn bench_encoding_type(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"strings/encoding_type\");\n    let int_enc = IntEncoder::plain();\n\n    for n in BENCHMARKED_LENGTHS {\n        let col = make_strings(n);\n        group.throughput(Throughput::Elements(n as u64));\n\n        let plain_bytes = encode_layer(\n            n,\n            vec![StagedProperty::str(\"name\", col.clone())],\n            ExplicitEncoder::all(int_enc),\n        );\n        group.bench_with_input(BenchmarkId::new(\"plain\", n), &plain_bytes, |b, bytes| {\n            b.iter(|| {\n                let layer = parser()\n                    .parse_layers(bytes)\n                    .expect(\"parse\")\n                    .remove(0)\n                    .into_layer01()\n                    .expect(\"layer01\");\n                let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                black_box(sum_str_lens(&parsed))\n            });\n        });\n\n        let fsst_bytes = encode_layer(\n            n,\n            vec![StagedProperty::str(\"name\", col)],\n            ExplicitEncoder::all_with_str(int_enc, StrEncoding::Fsst),\n        );\n        group.bench_with_input(BenchmarkId::new(\"fsst\", n), &fsst_bytes, |b, bytes| {\n            b.iter(|| {\n                let layer = parser()\n                    .parse_layers(bytes)\n                    .expect(\"parse\")\n                    .remove(0)\n                    .into_layer01()\n                    .expect(\"layer01\");\n                let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                black_box(sum_str_lens(&parsed))\n            });\n        });\n    }\n\n    group.finish();\n}\n\n/// Benchmark 4 – presence stream overhead: non-nullable vs nullable column\nfn bench_presence(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"strings/presence\");\n    let int_enc = IntEncoder::plain();\n\n    for n in BENCHMARKED_LENGTHS {\n        group.throughput(Throughput::Elements(n as u64));\n\n        // Non-nullable: no presence stream emitted.\n        let no_null_bytes = encode_layer(\n            n,\n            vec![StagedProperty::str(\"name\", make_strings(n))],\n            ExplicitEncoder::all(int_enc),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"no_nulls\", n),\n            &no_null_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n\n        // Nullable: presence stream present, every 5th entry is None.\n        let null_bytes = encode_layer(\n            n,\n            vec![StagedProperty::opt_str(\"name\", make_nullable_strings(n))],\n            ExplicitEncoder::all(int_enc),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"with_nulls\", n),\n            &null_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n    }\n\n    group.finish();\n}\n\n/// Benchmark 5 – shared dict vs plain\n///\n/// Compares decoding two plain string columns against a shared-dictionary struct\n/// column (plain and FSST flavors) that carries the same string data spread\n/// across two child sub-properties.\n/// Throughput is reported per *logical* string entry so all variants are comparable.\nfn bench_vs_shared_dict(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"strings/vs_shared_dict\");\n    let int_enc = IntEncoder::plain();\n\n    for n in BENCHMARKED_LENGTHS {\n        let total_entries = n * 2;\n        group.throughput(Throughput::Elements(total_entries as u64));\n\n        let col = make_strings(n);\n        let col_opt: Vec<Option<String>> = col.iter().map(|s| Some(s.clone())).collect();\n\n        // --- plain: two independent string columns ---\n        let plain_x2_bytes = encode_layer(\n            n,\n            vec![\n                StagedProperty::str(\"col1\", col.clone()),\n                StagedProperty::str(\"col2\", col.clone()),\n            ],\n            ExplicitEncoder::all(int_enc),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"plain_x2\", n),\n            &plain_x2_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n\n        // --- shared dict (plain) ---\n        //\n        // Two sub-properties; the second child has every 3rd entry as NULL so\n        // the child presence path is exercised.\n        let col2: Vec<Option<String>> = col\n            .iter()\n            .enumerate()\n            .map(|(i, s)| if i % 3 == 0 { None } else { Some(s.clone()) })\n            .collect();\n        let make_sd = || {\n            StagedSharedDict::new(\n                \"place:\",\n                [\n                    (\"type\", col_opt.clone(), Presence::AllPresent),\n                    (\"subtype\", col2.clone(), Presence::Mixed),\n                ],\n            )\n            .expect(\"StagedSharedDict::new failed\")\n        };\n\n        let sd_plain_bytes = encode_layer(\n            n,\n            vec![StagedProperty::SharedDict(make_sd())],\n            ExplicitEncoder::all(int_enc),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"shared_dict_plain\", n),\n            &sd_plain_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n\n        // --- shared dict (FSST) ---\n        let sd_fsst_bytes = encode_layer(\n            n,\n            vec![StagedProperty::SharedDict(make_sd())],\n            ExplicitEncoder::all_with_str(int_enc, StrEncoding::Fsst),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"shared_dict_fsst\", n),\n            &sd_fsst_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n\n        // --- FSST plain: two independent FSST-encoded columns ---\n        let fsst_x2_bytes = encode_layer(\n            n,\n            vec![\n                StagedProperty::str(\"col1\", col.clone()),\n                StagedProperty::str(\"col2\", col),\n            ],\n            ExplicitEncoder::all_with_str(int_enc, StrEncoding::Fsst),\n        );\n        group.bench_with_input(\n            BenchmarkId::new(\"fsst_x2\", n),\n            &fsst_x2_bytes,\n            |b, bytes| {\n                b.iter(|| {\n                    let layer = parser()\n                        .parse_layers(bytes)\n                        .expect(\"parse\")\n                        .remove(0)\n                        .into_layer01()\n                        .expect(\"layer01\");\n                    let parsed = layer.decode_all(&mut dec()).expect(\"decode_all\");\n                    black_box(sum_str_lens(&parsed))\n                });\n            },\n        );\n    }\n\n    group.finish();\n}\n\ncriterion_group!(\n    benches,\n    bench_plain_length_encoding,\n    bench_fsst_length_encoding,\n    bench_encoding_type,\n    bench_presence,\n    bench_vs_shared_dict,\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "rust/mlt-core/benches/decoding_utils.rs",
    "content": "use std::hint::black_box;\n\nuse criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};\nuse mlt_core::Decoder;\nuse mlt_core::wire::Morton;\n\nconst NUM_BITS: u32 = 15;\nconst COORDINATE_SHIFT: u32 = 1 << (NUM_BITS - 1);\n\n// This code runs in CI because of --all-targets, so make it run really fast.\n#[cfg(debug_assertions)]\npub const BENCHMARKED_LENGTHS: [u32; 1] = [1];\n#[cfg(not(debug_assertions))]\npub const BENCHMARKED_LENGTHS: [u32; 3] = [64, 256, 1024];\n\n/// Interleave `x` and `y` into a single Morton code using 15 bits per component.\n///\n/// Even bit positions encode `x`, odd positions encode `y`.\n/// This is the inverse of [`Morton::decode_codes`] / [`Morton::decode_delta`].\n#[must_use]\n#[inline]\npub fn encode_morton_15(x: u32, y: u32) -> u32 {\n    let mut code = 0u32;\n    for bit in 0..15 {\n        code |= ((x >> bit) & 1) << (2 * bit);\n        code |= ((y >> bit) & 1) << (2 * bit + 1);\n    }\n    code\n}\n\nfn make_morton_codes(n: u32) -> Vec<u32> {\n    (0..n)\n        .map(|i| {\n            let x = (i * 7 + 13) & 0x7FFF;\n            let y = (i * 11 + 31) & 0x7FFF;\n            encode_morton_15(x, y)\n        })\n        .collect()\n}\n\nfn make_morton_deltas(n: u32) -> Vec<u32> {\n    let codes = make_morton_codes(n);\n    let mut prev = 0i32;\n    codes\n        .iter()\n        .map(|&c| {\n            let delta = c.cast_signed().wrapping_sub(prev).cast_unsigned();\n            prev = c.cast_signed();\n            delta\n        })\n        .collect()\n}\n\nfn bench_impls<I: Clone, O>(\n    c: &mut Criterion,\n    group_name: &str,\n    make_input: impl Fn(u32) -> Vec<I>,\n    imp: impl Fn(&[I]) -> O,\n) {\n    let mut group = c.benchmark_group(group_name);\n    for n in BENCHMARKED_LENGTHS {\n        let input = make_input(n);\n        group.throughput(Throughput::Elements(u64::from(n)));\n        group.bench_with_input(BenchmarkId::new(\"impl\", n), &input, |b, input| {\n            b.iter_batched(\n                || input.clone(),\n                |i| black_box(imp(&i)),\n                BatchSize::SmallInput,\n            );\n        });\n    }\n    group.finish();\n}\n\nfn bench_morton(c: &mut Criterion) {\n    let meta = Morton::new(NUM_BITS, COORDINATE_SHIFT).unwrap();\n    bench_impls(c, \"morton/decode_codes\", make_morton_codes, |v| {\n        meta.decode_codes(v, &mut Decoder::with_max_size(u32::MAX))\n            .unwrap()\n    });\n\n    bench_impls(c, \"morton/decode_delta\", make_morton_deltas, |v| {\n        meta.decode_delta(v, &mut Decoder::with_max_size(u32::MAX))\n            .unwrap()\n    });\n}\n\ncriterion_group!(benches, bench_morton);\ncriterion_main!(benches);\n"
  },
  {
    "path": "rust/mlt-core/benches/encoding_e2e.rs",
    "content": "use std::hint::black_box;\n\nuse criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};\nuse mlt_core::__private::{dec, parser};\nuse mlt_core::Layer;\nuse mlt_core::encoder::{EncoderConfig, LogicalEncoder, stage_tile};\nuse strum::IntoEnumIterator as _;\n\n#[path = \"bench_utils.rs\"]\nmod bench_utils;\nuse bench_utils::{BENCHMARKED_ZOOM_LEVELS, load_mlt_tiles};\nuse mlt_core::encoder::SortStrategy::Unsorted;\nuse mlt_core::encoder::{\n    Codecs, Encoder, ExplicitEncoder, IntEncoder, PhysicalEncoder, StagedLayer,\n};\n\nfn limit<T>(values: impl Iterator<Item = T>) -> impl Iterator<Item = T> {\n    if cfg!(debug_assertions) {\n        values.take(1)\n    } else {\n        values.take(usize::MAX)\n    }\n}\n\n/// Build `StagedLayer` values from decoded tiles for encode benchmarks.\n///\n/// Goes through `Layer01 → TileLayer → StagedLayer`, which is the correct\n/// encode-pipeline entry point per CONTRIBUTING.md.\nfn decode_to_owned(tiles: &[(String, Vec<u8>)], tessellate: bool) -> Vec<StagedLayer> {\n    tiles\n        .iter()\n        .flat_map(|(_, data)| {\n            let mut d = dec();\n            let layers = parser().parse_layers(data).expect(\"mlt parse failed\");\n            layers\n                .into_iter()\n                .filter_map(|layer| {\n                    let Layer::Tag01(layer01) = layer else {\n                        return None;\n                    };\n                    let tile = layer01.into_tile(&mut d).ok()?;\n                    Some(stage_tile(tile, Unsorted, false, tessellate))\n                })\n                .collect::<Vec<_>>()\n        })\n        .collect()\n}\n\nfn bench_encode(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"mlt encode\");\n    for zoom in BENCHMARKED_ZOOM_LEVELS {\n        let tiles = load_mlt_tiles(zoom);\n        let total_bytes: usize = tiles.iter().map(|(_, d)| d.len()).sum();\n        group.throughput(Throughput::Bytes(total_bytes as u64));\n        for tessellate in [true, false] {\n            let enc_config = EncoderConfig {\n                tessellate,\n                ..Default::default()\n            };\n            for physical in limit(PhysicalEncoder::iter()) {\n                for logical in limit(LogicalEncoder::iter()) {\n                    let int_enc = IntEncoder::new(logical, physical);\n                    group.bench_with_input(\n                        BenchmarkId::new(\n                            format!(\"{logical:?}-{physical:?}/tessellate: {tessellate:?}\"),\n                            zoom,\n                        ),\n                        &tiles,\n                        |b, tiles| {\n                            b.iter_batched(\n                                || decode_to_owned(tiles, enc_config.tessellate),\n                                |layers| {\n                                    let mut codecs = Codecs::default();\n                                    for layer in layers {\n                                        let enc = Encoder::with_explicit(\n                                            enc_config,\n                                            ExplicitEncoder::all(int_enc),\n                                        );\n                                        black_box(\n                                            layer\n                                                .encode_into(enc, &mut codecs)\n                                                .expect(\"encode failed\"),\n                                        );\n                                    }\n                                },\n                                BatchSize::SmallInput,\n                            );\n                        },\n                    );\n                }\n            }\n        }\n    }\n\n    group.finish();\n}\n\ncriterion_group!(benches, bench_encode);\ncriterion_main!(benches);\n"
  },
  {
    "path": "rust/mlt-core/benches/encoding_from_mvt.rs",
    "content": "use std::hint::black_box;\n\nuse criterion::{BatchSize, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};\nuse mlt_core::encoder::EncoderConfig;\nuse mlt_core::mvt::mvt_to_tile_layers;\nuse mlt_core::{MltResult, TileLayer};\n\n#[path = \"bench_utils.rs\"]\nmod bench_utils;\nuse bench_utils::{BENCHMARKED_ZOOM_LEVELS, load_tiles};\n\n/// Load MVT tiles for a given zoom level from the OMT fixture directory.\nfn load_mvt_tiles(zoom: u8) -> Vec<(String, Vec<u8>)> {\n    load_tiles(zoom, \"fixtures/omt\", \".mvt\")\n}\n\n/// Parse MVT bytes into `TileLayer` objects outside the benchmark loop.\n///\n/// Errors are silently skipped (some fixture tiles may use unsupported geometry\n/// types); the benchmark only exercises successfully parsed layers.\nfn parse_mvt_to_tile_layers(mvt_files: &[(String, Vec<u8>)]) -> Vec<TileLayer> {\n    mvt_files\n        .iter()\n        .flat_map(|(path, data)| {\n            mvt_to_tile_layers(data.clone()).unwrap_or_else(|err| {\n                eprintln!(\"skipping {path}: {err}\");\n                Vec::new()\n            })\n        })\n        .collect()\n}\n\nfn bench_encode_from_mvt(c: &mut Criterion) {\n    let mut group = c.benchmark_group(\"mlt encode from mvt\");\n    let cfg = EncoderConfig {\n        tessellate: true,\n        ..Default::default()\n    };\n\n    for zoom in BENCHMARKED_ZOOM_LEVELS {\n        let mvt_files = load_mvt_tiles(zoom);\n        let total_bytes: usize = mvt_files.iter().map(|(_, d)| d.len()).sum();\n\n        // Parse all MVT files into TileLayer once, outside every benchmark iteration.\n        let tile_layers: Vec<TileLayer> = parse_mvt_to_tile_layers(&mvt_files);\n\n        group.throughput(Throughput::Bytes(total_bytes as u64));\n\n        group.bench_with_input(BenchmarkId::new(\"zoom\", zoom), &tile_layers, |b, layers| {\n            b.iter_batched(\n                // Setup: clone pre-parsed layers so encode (which takes `self`) can consume them.\n                || layers.clone(),\n                // Benchmark: encode every layer.\n                |layers| {\n                    let result: MltResult<Vec<Vec<u8>>> = layers\n                        .into_iter()\n                        .map(|layer| layer.encode(black_box(cfg)))\n                        .collect();\n                    black_box(result.expect(\"encode failed\"));\n                },\n                BatchSize::LargeInput,\n            );\n        });\n    }\n\n    group.finish();\n}\n\ncriterion_group!(benches, bench_encode_from_mvt);\ncriterion_main!(benches);\n"
  },
  {
    "path": "rust/mlt-core/fuzz/.gitignore",
    "content": "target\ncorpus\nartifacts\ncoverage\nCargo.lock\n"
  },
  {
    "path": "rust/mlt-core/fuzz/Cargo.toml",
    "content": "[package]\nname = \"fuzz\"\nversion = \"0.0.0\"\npublish = false\nedition = \"2024\"\n\n[package.metadata]\ncargo-fuzz = true\n\n[workspace]\n\n[lib]\nname = \"mlt_fuzz\"\npath = \"src/lib.rs\"\n\n[[bin]]\nname = \"layer\"\npath = \"fuzz_targets/layer.rs\"\ntest = false\ndoc = false\nbench = false\n\n[[bin]]\nname = \"decoded_layer\"\npath = \"fuzz_targets/decoded_layer.rs\"\ntest = false\ndoc = false\nbench = false\n\n[dependencies]\narbitrary = { version = \"1.4.2\", features = [\"derive\"] }\nhex = \"0.4.3\"\nlibfuzzer-sys = \"0.4\"\nmlt-core = { path = \"..\", features = [\"arbitrary\", \"__private\"] }\npretty_assertions = \"1.4\"\n\n[lints.rust]\nunsafe_code = \"forbid\"\nunused_qualifications = \"warn\"\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(fuzzing)'] }\n\n[lints.clippy]\ncargo = { level = \"warn\", priority = -1 }\npedantic = { level = \"warn\", priority = -1 }\n\n# Restrictions\npanic_in_result_fn = \"warn\"\ntodo = \"warn\"\nunimplemented = \"warn\"\nunused_trait_names = \"warn\"\n\n# Too noisy\nmissing_errors_doc = \"allow\"\nmissing_panics_doc = \"allow\"\nmultiple_crate_versions = \"allow\"\ntoo_many_lines = \"allow\"\n"
  },
  {
    "path": "rust/mlt-core/fuzz/README.md",
    "content": "# Fuzzing for mlt\n\nThis directory contains fuzzing infrastructure for the `mlt` parser using [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz) and [libFuzzer](https://llvm.org/docs/LibFuzzer.html).\n\n## Overview\n\nFuzzing is a software testing technique that provides random or semi-random data as input to find bugs, crashes, and security vulnerabilities.\nThis fuzzer tests the round-trip property of the MapLibre Tile parser:\nAny data that can be successfully parsed should be serializable back to bytes that match the original input.\n\n## Subproject Structure\n\n```\nfuzz/\n├── src/\n│   └── lib.rs              # mlt_fuzz library with LayerInput and fuzz_roundtrip logic\n├── fuzz_targets/\n│   └── layer.rs            # Fuzz target that feeds random data to LayerInput\n├── tests/\n│   └── reproduce.rs        # Template for reproducing fuzzer-found issues\n├── corpus/layer/           # Seed inputs for fuzzing\n└── artifacts/              # Crash-inducing inputs discovered by fuzzer\n```\n\n## What is Being Tested\n\nThe fuzzer validates the following property:\n\n```\nparse(bytes) -> Layer -> write_to(buffer) -> bytes'\nassert_eq!(bytes, bytes')\n```\n\nThis ensures that:\n1. The parser correctly interprets the binary format\n2. The serializer produces canonical output\n3. No data is lost or corrupted during the parse → serialize round-trip\n\n## Prerequisites\n\nInstall `cargo-fuzz`:\n\n```bash\ncargo install cargo-fuzz\n```\n\n> [!NOTE]\n> `cargo-fuzz` requires a nightly Rust toolchain.\n\n## Running the Fuzzer\n\nFrom the `fuzz` directory:\n\n```bash\ncargo +nightly fuzz run layer\n```\n\nPopular options:\n- Run count: `-- -runs=1000000`\n- Timeout per input: `-- -timeout=10`\n\n## Fuzz Targets\n\n### `layer`\n\n**Location:** `fuzz_targets/layer.rs`\n\nTests the `Layer` parser and serializer by generating arbitrary `LayerInput` values and calling `fuzz_roundtrip()` on them.\n\nIf a mismatch is found, the fuzzer panics with a detailed error message showing both the input and output in hexadecimal format.\n\n## Corpus\n\nThe `corpus/layer` directory contains input files that have been discovered during fuzzing. These serve as:\n- Seeds for future fuzzing runs\n- Regression tests to ensure previously found issues don't reoccur\n- Examples of valid inputs\n\nThe corpus is currently empty but will be populated as fuzzing discovers interesting inputs.\n\n## Artifacts\n\nWhen the fuzzer discovers a crash or failure, it saves the triggering input to the `artifacts` directory. Each artifact file contains the exact byte sequence that caused the issue.\n\n## Reproducing Failures\n\nWhen the fuzzer finds an issue, you can reproduce it using the test infrastructure:\n\n1. The failing input is saved to `artifacts/layer/crash-<hash>`\n2. Minimize the input using `cargo fuzz tmin layer artifacts/layer/crash-<hash>`\n3. Edit `tests/reproduce.rs` and update the filename:\n   ```rust,no_compile\n   let bytes = include_bytes!(\"../artifacts/layer/minimized-from-<hash>\");\n   ```\n4. Run the test:\n   ```bash\n   cargo test\n   ```\n\nThis approach allows you to:\n- Debug the issue with full Rust tooling (not just nightly)\n- Set breakpoints and use a debugger\n- Iterate on fixes quickly\n\nAlternatively, you can run the fuzzer directly with the artifact:\n\n```bash\ncargo +nightly fuzz run layer artifacts/layer/crash-<hash>\n```\n\n## Coverage\n\nTo generate coverage information:\n\n```bash\ncargo +nightly fuzz coverage layer\n```\n\nThis creates coverage data in the `coverage` directory, showing which code paths have been exercised during fuzzing.\n\n## Adding New Fuzz Targets\n\nTo add a new fuzz target:\n\n1. Create a new file in `fuzz_targets/`:\n   ```bash\n   cargo fuzz add <target_name>\n   ```\n2. Implement the fuzz target using `fuzz_target!` macro\n3. Add corresponding test infrastructure in `src/lib.rs` and `tests/`\n\n## Further Reading\n\n- [cargo-fuzz documentation](https://rust-fuzz.github.io/book/cargo-fuzz.html)\n- [libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html)\n- [mlt library documentation](../README.md)\n"
  },
  {
    "path": "rust/mlt-core/fuzz/fuzz_targets/decoded_layer.rs",
    "content": "#![no_main]\n\nuse libfuzzer_sys::fuzz_target;\nuse mlt_fuzz::DecodedLayerInput;\n\nfuzz_target!(|input: DecodedLayerInput| {\n    input.fuzz_roundtrip();\n});\n"
  },
  {
    "path": "rust/mlt-core/fuzz/fuzz_targets/layer.rs",
    "content": "#![no_main]\n\nuse libfuzzer_sys::fuzz_target;\nuse mlt_fuzz::LayerInput;\n\nfuzz_target!(|input: LayerInput| {\n    input.fuzz_roundtrip();\n});\n"
  },
  {
    "path": "rust/mlt-core/fuzz/src/decoded_layer.rs",
    "content": "use mlt_core::encoder::SortStrategy::Unsorted;\nuse mlt_core::encoder::{Codecs, Encoder, StagedLayer, stage_tile};\nuse mlt_core::{Decoder, Layer, Parser, TileLayer};\n\n/// Fuzz input that starts from a staged layer and tests encode → decode roundtrip.\n///\n/// Generates valid [`StagedLayer`] values directly and verifies that the\n/// canonical roundtrip (`Tile -> Staged -> bytes -> Tile`) is idempotent.\npub struct DecodedLayerInput {\n    pub layer: StagedLayer,\n}\n\nimpl arbitrary::Arbitrary<'_> for DecodedLayerInput {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {\n        let layer: StagedLayer = u.arbitrary()?;\n        Ok(Self { layer })\n    }\n}\n\nimpl DecodedLayerInput {\n    pub fn fuzz_roundtrip(self) {\n        // Normalize: encode the fuzzed StagedLayer and decode to TileLayer.\n        // This drops all-null columns, etc. — expected encoder behavior.\n        let tile1 = encode_decode(self.layer);\n        let tile2 = encode_decode(stage_tile(tile1, Unsorted, false, false));\n\n        // Same roundtrip again — must be a fixpoint.\n        let tile3 = encode_decode(stage_tile(tile2.clone(), Unsorted, false, false));\n        assert_eq!(tile2, tile3, \"canonical roundtrip is not idempotent\");\n    }\n}\n\n/// Encode a [`StagedLayer`] to bytes, then parse and decode back to a\n/// row-oriented [`TileLayer`].\nfn encode_decode(staged: StagedLayer) -> TileLayer {\n    let mut codecs = Codecs::default();\n    let buffer = staged\n        .encode_into(Encoder::default(), &mut codecs)\n        .expect(\"encode should not fail\")\n        .into_layer_bytes()\n        .expect(\"into_layer_bytes should not fail\");\n\n    let mut layers = Parser::default()\n        .parse_layers(&buffer)\n        .expect(\"layer must re-parse\");\n    assert_eq!(layers.len(), 1, \"expected exactly one layer\");\n    let Layer::Tag01(lazy) = layers.remove(0) else {\n        panic!(\"expected Tag01 layer\");\n    };\n    lazy.into_tile(&mut Decoder::default())\n        .expect(\"into_tile should not fail\")\n}\n\nimpl std::fmt::Debug for DecodedLayerInput {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"DecodedLayerInput {{\\n\\tlayer: {:#?}\\n}}\", self.layer)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/fuzz/src/layer.rs",
    "content": "use hex::ToHex as _;\nuse mlt_core::{Decoder, Parser};\n\n#[derive(arbitrary::Arbitrary)]\npub struct LayerInput {\n    pub bytes: Vec<u8>,\n}\nimpl std::fmt::Debug for LayerInput {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{:?}\", self.bytes.encode_hex::<String>())\n    }\n}\nimpl LayerInput {\n    pub fn fuzz_roundtrip(self) {\n        let Ok(layers) = Parser::default().parse_layers(&self.bytes) else {\n            return;\n        };\n        for layer in layers {\n            let _ = layer.decode_all(&mut Decoder::default());\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/fuzz/src/lib.rs",
    "content": "mod decoded_layer;\nmod layer;\npub use decoded_layer::*;\npub use layer::*;\n"
  },
  {
    "path": "rust/mlt-core/fuzz/tests/reproduce.rs",
    "content": "use mlt_fuzz::LayerInput;\n\n/// Template for reproducing fuzzer-found issues\n///\n/// When the fuzzer finds a crash, replace the filename below with the artifact path:\n/// 1. Update the filename: `../artifacts/layer/crash-abc123`\n/// 2. Run: `cargo test --manifest-path fuzz/Cargo.toml`\n#[test]\nfn fuzz_roundtrip() {\n    let bytes = include_bytes!(\"../artifacts/layer/crash-abc123\");\n    let input = LayerInput {\n        bytes: bytes.to_vec(),\n    };\n    input.fuzz_roundtrip();\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/bytes.rs",
    "content": "use crate::MltError::BufferUnderflow;\nuse crate::utils::{AsUsize as _, take};\nuse crate::{Decoder, MltRefResult, MltResult};\n\n/// Pack bools into bytes where each byte represents 8 booleans.\npub fn encode_bools_to_bytes(\n    bools: impl ExactSizeIterator<Item = bool>,\n    target: &mut Vec<u8>,\n) -> &[u8] {\n    let num_bytes = bools.len().div_ceil(8);\n    target.clear();\n    target.resize(num_bytes, 0u8);\n    for i in bools.enumerate().filter_map(|(i, bit)| bit.then_some(i)) {\n        target[i / 8] |= 1 << (i % 8);\n    }\n    target\n}\n\n/// Decode a slice of bytes into a vector of u64 values assuming little-endian encoding\n/// TODO: Should this return `MltRefResult`, or should it assert the entire input is consumed?\npub fn decode_bytes_to_u64s<'a>(\n    mut input: &'a [u8],\n    num_values: u32,\n    dec: &mut Decoder,\n) -> MltRefResult<'a, Vec<u64>> {\n    let Some(expected_bytes) = num_values.checked_mul(8) else {\n        return Err(BufferUnderflow(u32::MAX, input.len()));\n    };\n    if input.len() < expected_bytes.as_usize() {\n        return Err(BufferUnderflow(expected_bytes, input.len()));\n    }\n\n    let alloc_size = num_values.as_usize();\n    let mut values = dec.alloc(alloc_size)?;\n\n    for _ in 0..num_values {\n        let (new_input, bytes) = take(input, 8)?;\n        let value = u64::from_le_bytes([\n            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],\n        ]);\n        values.push(value);\n        input = new_input;\n    }\n\n    debug_assert_length(&values, alloc_size);\n    Ok((input, values))\n}\n\n/// Decode a slice of bytes into a vector of u32 values assuming little-endian encoding\n/// FIXME: ensure the entire input is consumed, and don't return it?\npub fn decode_bytes_to_u32s<'a>(\n    mut input: &'a [u8],\n    num_values: u32,\n    dec: &mut Decoder,\n) -> MltRefResult<'a, Vec<u32>> {\n    let Some(expected_bytes) = num_values.checked_mul(4) else {\n        return Err(BufferUnderflow(u32::MAX, input.len()));\n    };\n    if input.len() < expected_bytes.as_usize() {\n        return Err(BufferUnderflow(expected_bytes, input.len()));\n    }\n\n    let alloc_size = num_values.as_usize();\n    let mut values = dec.alloc(alloc_size)?;\n\n    for _ in 0..num_values {\n        let (new_input, bytes) = take(input, 4)?;\n        let value = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);\n        values.push(value);\n        input = new_input;\n    }\n\n    debug_assert_length(&values, alloc_size);\n    Ok((input, values))\n}\n\n/// Helper to unpack a `Vec<u8>` into `Vec<bool>` where each byte represents 8 booleans.\n/// TODO: Use `BitSlice` from bitvec crate and avoid copying?\npub fn decode_bytes_to_bools(\n    bytes: &[u8],\n    num_bools: usize,\n    dec: &mut Decoder,\n) -> MltResult<Vec<bool>> {\n    if num_bools > bytes.len() * 8 {\n        return Err(BufferUnderflow(\n            u32::try_from(num_bools.div_ceil(8))?,\n            bytes.len(),\n        ));\n    }\n    let mut result = dec.alloc(num_bools)?;\n    for i in 0..num_bools {\n        result.push((bytes[i / 8] >> (i % 8)) & 1 == 1);\n    }\n    debug_assert_length(&result, num_bools);\n    Ok(result)\n}\n\n#[inline]\npub fn debug_assert_length<T>(buffer: &[T], expected_len: usize) {\n    debug_assert_eq!(\n        buffer.len(),\n        expected_len,\n        \"Expected buffer to have exact length\"\n    );\n}\n\n#[cfg(test)]\nmod tests {\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::test_helpers::{assert_empty, dec};\n\n    proptest! {\n        #[test]\n        fn encode_bools_to_bytes_roundtrip(bools: Vec<bool>) {\n            let mut bytes = Vec::new();\n            let data = encode_bools_to_bytes(bools.iter().copied(), &mut bytes);\n            let bools_rountrip = decode_bytes_to_bools(data, bools.len(), &mut dec()).unwrap();\n            prop_assert_eq!(bools_rountrip, bools);\n        }\n\n        #[test]\n        fn test_u32_bytes_roundtrip(data: Vec<u32>) {\n            let mut encoded = Vec::with_capacity(data.len() * 4);\n            for val in &data {\n                encoded.extend_from_slice(&val.to_le_bytes());\n            }\n            let decoded = assert_empty(decode_bytes_to_u32s(&encoded, u32::try_from(data.len()).unwrap(), &mut dec()));\n            prop_assert_eq!(data, decoded);\n        }\n\n        #[test]\n        fn test_u64_bytes_roundtrip(data: Vec<u64>) {\n            let mut encoded = Vec::with_capacity(data.len() * 8);\n            for val in &data {\n                encoded.extend_from_slice(&val.to_le_bytes());\n            }\n            let decoded = assert_empty(decode_bytes_to_u64s(&encoded, u32::try_from(data.len()).unwrap(), &mut dec()));\n            prop_assert_eq!(data, decoded);\n        }\n    }\n\n    #[test]\n    fn test_bytes_to_u32s_valid() {\n        // Little-endian representation:\n        // [0x04, 0x03, 0x02, 0x01] -> 0x01020304\n        // [0xDD, 0xCC, 0xBB, 0xAA] -> 0xAABBCCDD\n        let bytes: [u8; 8] = [0x04, 0x03, 0x02, 0x01, 0xDD, 0xCC, 0xBB, 0xAA];\n        let u32s = assert_empty(decode_bytes_to_u32s(&bytes, 2, &mut dec()));\n        assert_eq!(\n            u32s,\n            vec![0x0102_0304, 0xAABB_CCDD],\n            \"Decoded values should match\"\n        );\n    }\n\n    #[test]\n    fn test_bytes_to_u32s_empty() {\n        let bytes: [u8; 0] = [];\n        let u32s = assert_empty(decode_bytes_to_u32s(&bytes, 0, &mut dec()));\n        assert!(\n            u32s.is_empty(),\n            \"Output should be an empty Vec for 0 values\"\n        );\n    }\n\n    #[test]\n    fn test_bytes_to_u32s_buffer_underflow() {\n        // Only 4 bytes but requesting 2 values (8 bytes needed)\n        let bytes = [0x01, 0x02, 0x03, 0x04];\n        let res = decode_bytes_to_u32s(&bytes, 2, &mut dec());\n        assert!(\n            res.is_err(),\n            \"Should error if not enough bytes for requested values\"\n        );\n    }\n\n    #[test]\n    fn test_bytes_to_u32s_partial_consumption() {\n        // 12 bytes (3 values) but only requesting 2 values\n        let bytes: [u8; 12] = [\n            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n        ];\n        let res = decode_bytes_to_u32s(&bytes, 2, &mut dec());\n        assert!(res.is_ok(), \"Should decode 2 values from larger buffer\");\n        let (remaining, u32s) = res.unwrap();\n        assert_eq!(remaining.len(), 4, \"Should have 4 bytes remaining\");\n        assert_eq!(u32s.len(), 2);\n        assert_eq!(u32s, vec![0x0403_0201, 0x0807_0605]);\n    }\n\n    #[test]\n    fn test_decode_u32() {\n        let bytes = [1, 0, 0, 0, 2, 0, 0, 0];\n        let expected = (&[][..], vec![1, 2]);\n        let decoded = decode_bytes_to_u32s(&bytes, 2, &mut dec()).unwrap();\n        assert_eq!(decoded, expected);\n    }\n\n    #[test]\n    fn test_decode_u64() {\n        let bytes = [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0];\n        let expected = (&[][..], vec![1, 2]);\n        let decoded = decode_bytes_to_u64s(&bytes, 2, &mut dec()).unwrap();\n        assert_eq!(decoded, expected);\n    }\n\n    #[test]\n    fn test_decode_bytes_to_u32s_empty() {\n        let decoded = assert_empty(decode_bytes_to_u32s(&[], 0, &mut dec()));\n        assert!(decoded.is_empty());\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/fastpfor.rs",
    "content": "use fastpfor::{AnyLenCodec as _, FastPFor256};\n\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltError, MltResult};\n\n/// Decode `FastPFOR`-compressed data using the composite codec protocol.\n///\n/// The Java MLT encoder uses `Composition(FastPFOR(), VariableByte())`, matching\n/// the C++ `CompositeCodec<FastPFor<8>, VariableByte>`. The wire format is:\n///\n/// 1. First u32 = number of compressed u32 words from the primary codec (`FastPFor`)\n/// 2. Next N u32 words = primary codec (`FastPFor`) compressed data\n/// 3. Remaining u32 words = secondary codec (`VByte`) compressed data\n///\n/// The compressed bytes are stored as big-endian u32 values by the Java encoder.\npub fn decode_fastpfor(data: &[u8], num_values: u32, dec: &mut Decoder) -> MltResult<Vec<u32>> {\n    if num_values == 0 {\n        // FIXME: eventually there should not be a header anywhere at all\n        return if data.is_empty() {\n            Ok(vec![])\n        } else {\n            Err(MltError::InvalidFastPforByteLength(0))\n        };\n    }\n\n    // Convert big-endian bytes to u32 values\n    if !data.len().is_multiple_of(4) {\n        return Err(MltError::InvalidFastPforByteLength(data.len()));\n    }\n    // The Java MLT encoder writes compressed int[] → byte[] in big-endian order.\n    // We must convert BE bytes → u32 to reconstruct the original integer values\n    // that the Composition(FastPFOR, VariableByte) codec produced.\n    let num_words = data.len() / 4;\n    dec.consume_items::<u32>(num_words)?;\n    let input: Vec<u32> = (0..num_words)\n        .map(|i| {\n            let o = i * 4;\n            u32::from_be_bytes([data[o], data[o + 1], data[o + 2], data[o + 3]])\n        })\n        .collect();\n\n    let mut result = Vec::new();\n    FastPFor256::default().decode(&input, &mut result, Some(num_values))?;\n\n    let Some(adjustment) = result\n        .len()\n        .checked_sub(num_values.as_usize())\n        .and_then(|v| u32::try_from(v).ok())\n    else {\n        return Err(MltError::FastPforDecode(num_values, result.len()));\n    };\n\n    dec.adjust(adjustment);\n    result.truncate(num_values.as_usize());\n\n    Ok(result)\n}\n\n#[cfg(test)]\nmod tests {\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::test_helpers::dec;\n\n    proptest! {\n        #[test]\n        fn test_fastpfor_roundtrip(data: Vec<u32>) {\n            // FastPFor256 produces a non-empty output (VByte header) even for empty input,\n            // but decode_fastpfor requires zero bytes when num_values == 0 — consistent\n            // with how PhysicalEncoder guards `if !values.is_empty()`.\n            prop_assume!(!data.is_empty());\n            let mut encoded = Vec::new();\n            FastPFor256::default().encode(&data, &mut encoded).unwrap();\n            // Convert u32 words to big-endian bytes to match the wire format.\n            let mut encoded2 = Vec::with_capacity(encoded.len() * 4);\n            for word in &encoded {\n                encoded2.extend_from_slice(&word.to_be_bytes());\n            }\n            let decoded = decode_fastpfor(&encoded2, data.len().try_into().unwrap(), &mut dec()).unwrap();\n            prop_assert_eq!(data, decoded);\n        }\n    }\n\n    #[test]\n    fn test_decode_fastpfor_empty() {\n        let decoded = decode_fastpfor(&[], 0, &mut dec()).unwrap();\n        assert!(decoded.is_empty());\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/fsst.rs",
    "content": "use crate::decoder::RawFsstData;\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltResult};\n\n/// Decode an FSST-compressed byte sequence into the original bytes and value lengths,\n/// charging `dec` for the output.\n///\n/// Takes a `RawFsstData` which provides the 4 streams needed for FSST decoding:\n/// - `symbol_lengths`: per-symbol byte lengths (decoded as u32 values)\n/// - `symbol_table`: concatenated raw symbol bytes (read as raw bytes)\n/// - `lengths`: original string byte lengths (decoded as u32 values)\n/// - `corpus`: the FSST-encoded payload (read as raw bytes)\n///\n/// The encoding uses two special cases:\n/// - Byte `0xFF` (255): the next byte is a literal — output it verbatim.\n/// - Any other byte `idx < symbol_lengths.len()`: expand the symbol at that index.\n///\n/// Returns `(decompressed_utf8_string, value_lengths)`.\npub fn decode_fsst(raw: RawFsstData<'_>, dec: &mut Decoder) -> MltResult<(String, Vec<u32>)> {\n    let RawFsstData {\n        symbol_lengths,\n        symbol_table,\n        lengths,\n        corpus,\n    } = raw;\n\n    let sym_lens = symbol_lengths.decode_u32s(dec)?;\n    let symbols = symbol_table.data;\n    let compressed = corpus.data;\n\n    // Build symbol offset table from lengths.\n    let mut symbol_offsets = vec![0u32; sym_lens.len()];\n    for i in 1..sym_lens.len() {\n        symbol_offsets[i] = symbol_offsets[i - 1] + sym_lens[i - 1];\n    }\n\n    let mut output = Vec::new();\n    let mut i = 0;\n    while i < compressed.len() {\n        let sym_idx = usize::from(compressed[i]);\n        if sym_idx == 255 {\n            i += 1;\n            output.push(compressed[i]);\n        } else if sym_idx < sym_lens.len() {\n            let len = sym_lens[sym_idx].as_usize();\n            let off = symbol_offsets[sym_idx].as_usize();\n            output.extend_from_slice(&symbols[off..off + len]);\n        }\n        i += 1;\n    }\n\n    dec.consume_items::<u8>(output.len())?;\n    Ok((String::from_utf8(output)?, lengths.decode_u32s(dec)?))\n}\n\n/// Raw output from FSST compression (unencoded byte buffers).\n///\n/// Pass to the string encoder's `write_fsst_data` helper to write these\n/// streams directly to an [`Encoder`](crate::encoder::Encoder).\npub struct FsstRawData {\n    /// Per-symbol byte lengths (to be written as `Length(Symbol)` stream).\n    pub symbol_lengths: Vec<u32>,\n    /// Concatenated raw symbol bytes (to be written as `Data(Fsst)` stream).\n    pub symbol_bytes: Vec<u8>,\n    /// Per-value byte lengths of the compressed corpus (to be written as `Length(Dictionary)` stream).\n    pub value_lengths: Vec<u32>,\n    /// FSST-compressed corpus bytes (to be written as `Data(dict_type)` stream).\n    pub corpus: Vec<u8>,\n}\n\n/// Shared FSST compression kernel: train a compressor on `values` and compress the corpus.\n///\n/// Returns [`FsstRawData`] with the four raw byte/int buffers ready to be written to\n/// an encoder via the caller's chosen integer encoders.\n///\n/// Stream order when written:\n/// 1. Symbol lengths (`Length(Symbol)`)\n/// 2. Symbol table data (`Data(Fsst)`)\n/// 3. Value lengths (`Length(Dictionary)`)\n/// 4. Compressed corpus (`Data(dict_type)` — supplied by the caller at write time)\n///\n/// Note: The FSST algorithm implementation may differ from Java's, so the\n/// compressed output may not be byte-for-byte identical. Both implementations\n/// are semantically compatible and can decode each other's output.\npub fn compress_fsst<S: AsRef<str>>(values: &[S]) -> FsstRawData {\n    let byte_slices: Vec<&[u8]> = values.iter().map(|s| s.as_ref().as_bytes()).collect();\n    let compressor = fsst::Compressor::train(&byte_slices);\n    compress_fsst_with(values, &compressor)\n}\n\n/// Like [`compress_fsst`] but reuses an already-trained [`fsst::Compressor`].\npub fn compress_fsst_with<S: AsRef<str>>(\n    values: &[S],\n    compressor: &fsst::Compressor,\n) -> FsstRawData {\n    let symbols = compressor.symbol_table();\n    let symbol_lengths_u8 = compressor.symbol_lengths();\n\n    let mut symbol_bytes = Vec::new();\n    for sym in symbols {\n        let bytes = sym.to_u64().to_le_bytes();\n        let len = sym.len();\n        symbol_bytes.extend_from_slice(&bytes[..len]);\n    }\n\n    let symbol_lengths: Vec<u32> = symbol_lengths_u8\n        .iter()\n        .take(symbols.len())\n        .map(|&l| u32::from(l))\n        .collect();\n\n    let value_lengths: Vec<u32> = values\n        .iter()\n        .map(|s| u32::try_from(s.as_ref().len()).unwrap_or(u32::MAX))\n        .collect();\n\n    // Compress all strings as one concatenated buffer.\n    // This allows FSST symbol matches across string boundaries.\n    // For example: `\"sdfAAAA\" + \"AAAAyxc\"` may now compress more `A`s.\n    // The decoder decompresses the full corpus and splits by original (uncompressed) value lengths.\n    let concatenated: Vec<u8> = values\n        .iter()\n        .flat_map(|s| s.as_ref().as_bytes())\n        .copied()\n        .collect();\n    let corpus = compressor.compress(&concatenated);\n\n    FsstRawData {\n        symbol_lengths,\n        symbol_bytes,\n        value_lengths,\n        corpus,\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use rstest::rstest;\n\n    use super::*;\n    use crate::decoder::{DictionaryType, LengthType, RawFsstData, RawStream, StreamType};\n    use crate::encoder::model::StreamCtx;\n    use crate::encoder::{Codecs, EncodedStream, Encoder, ExplicitEncoder, IntEncoder};\n    use crate::test_helpers::{assert_empty, dec, parser};\n    use crate::utils::BinarySerializer as _;\n\n    /// Encode the 4 FSST raw streams to wire bytes and parse them back for decoding.\n    fn roundtrip(values: &[&str]) -> (String, Vec<u32>) {\n        use crate::decoder::StreamMeta;\n        let raw = compress_fsst(values);\n\n        let sym_len_bytes = {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint()),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop(StreamType::Length(LengthType::Symbol), \"symbol\");\n            codecs\n                .write_int_stream(&raw.symbol_lengths, &ctx, &mut enc)\n                .unwrap();\n            enc.data\n        };\n        let sym_table_stream = EncodedStream {\n            meta: StreamMeta::new_none(\n                StreamType::Data(DictionaryType::Fsst),\n                raw.symbol_lengths.len(),\n            )\n            .unwrap(),\n            data: raw.symbol_bytes.clone(),\n        };\n        let lengths_bytes = {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint()),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop(StreamType::Length(LengthType::Dictionary), \"dictionary\");\n            codecs\n                .write_int_stream(&raw.value_lengths, &ctx, &mut enc)\n                .unwrap();\n            enc.data\n        };\n        let corpus_stream = EncodedStream {\n            meta: StreamMeta::new_none(StreamType::Data(DictionaryType::Single), values.len())\n                .unwrap(),\n            data: raw.corpus.clone(),\n        };\n\n        let mut sym_table_buf = Vec::new();\n        sym_table_buf\n            .write_stream(&sym_table_stream)\n            .expect(\"write_stream failed\");\n        let mut corpus_buf = Vec::new();\n        corpus_buf\n            .write_stream(&corpus_stream)\n            .expect(\"write_stream failed\");\n        let buffers = [sym_len_bytes, sym_table_buf, lengths_bytes, corpus_buf];\n        let mut raw_streams = Vec::new();\n        for buf in &buffers {\n            raw_streams.push(assert_empty(RawStream::from_bytes(buf, &mut parser())));\n        }\n        let [s0, s1, s2, s3] = raw_streams.try_into().expect(\"expected 4 streams\");\n        let raw = RawFsstData::new(s0, s1, s2, s3).expect(\"RawFsstData::new failed\");\n        decode_fsst(raw, &mut dec()).expect(\"decode_fsst failed\")\n    }\n\n    #[test]\n    fn test_fsst_roundtrip_empty() {\n        let (corpus, lengths) = roundtrip(&[]);\n        assert!(corpus.is_empty());\n        assert!(lengths.is_empty());\n    }\n\n    #[rstest]\n    #[case::longer(&[\"hello world\", \"hello rust\", \"hello fsst\", \"world\"])]\n    #[case::short(&[\"hello\"])]\n    fn automatic_optimization_roundtrip(#[case] values: &[&str]) {\n        let (corpus, lengths) = roundtrip(values);\n        let mut offset = 0;\n        for (s, &len) in values.iter().zip(&lengths) {\n            let len = len as usize;\n            assert_eq!(&corpus[offset..offset + len], *s);\n            offset += len;\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/hilbert.rs",
    "content": "use geo_types::Coord;\n\nuse crate::encoder::model::CurveParams;\n\n/// Return the 1-D Hilbert curve index for `(x, y)` at the given `level`.\n///\n/// The grid has side `2^level`; both `x` and `y` must be in `[0, 2^level)`,\n/// and `level` must be in `[1, 16]`.  The returned index is in\n/// `[0, 4^level)` and fits in a `u32` for all valid levels.\n#[must_use]\npub fn hilbert_xy_to_index(level: u32, coord: Coord<u32>) -> u32 {\n    debug_assert!((1..=16).contains(&level), \"level must be in [1, 16]\");\n    debug_assert!(coord.x < (1 << level), \"x out of range for level\");\n    debug_assert!(coord.y < (1 << level), \"y out of range for level\");\n\n    hilbert_2d::u32::xy2h_discrete(coord.x, coord.y, level, hilbert_2d::Variant::Hilbert)\n}\n\n/// Compute a Hilbert curve sort key from signed integer coordinates.\n///\n/// `shift` is added to both axes to move the origin into the non-negative\n/// range (use the global minimum across *all* vertices for the layer, not\n/// per-axis).  `bits` is the grid level — both shifted components must\n/// fit in `[0, 2^bits)`.\n///\n/// Use [`hilbert_curve_params_from_bounds`] to compute `shift` and `bits`\n/// from global min/max coordinates.\n#[must_use]\npub fn hilbert_sort_key(c: Coord<i32>, params: CurveParams) -> u32 {\n    debug_assert!((1..=16).contains(&params.bits));\n    #[expect(\n        clippy::cast_possible_truncation,\n        clippy::cast_sign_loss,\n        reason = \"shift brings value into [0, extent]; masked to 16 bits immediately after\"\n    )]\n    let sx = ((i64::from(c.x) + i64::from(params.shift)) as u32) & 0xFFFF;\n    #[expect(\n        clippy::cast_possible_truncation,\n        clippy::cast_sign_loss,\n        reason = \"shift brings value into [0, extent]; masked to 16 bits immediately after\"\n    )]\n    let sy = ((i64::from(c.y) + i64::from(params.shift)) as u32) & 0xFFFF;\n    hilbert_xy_to_index(params.bits, (sx, sy).into())\n}\n\n/// Compute the coordinate shift and grid level from pre-computed global\n/// min/max values across all vertex coordinates (both axes combined).\n///\n/// Returns `(shift, bits)` where:\n/// - `shift` is subtracted from the global minimum (i.e. it equals\n///   `min_val.unsigned_abs()` when `min_val < 0`, else `0`), ensuring all\n///   shifted coordinates are non-negative.\n/// - `bits` is the smallest level `l` in `[1, 16]` such that all\n///   shifted values fit in `[0, 2^l)`.\n///\n/// If `min > max` (empty input), returns `(0, 1)`.\n#[must_use]\npub fn hilbert_curve_params_from_bounds(min_val: i32, max_val: i32) -> CurveParams {\n    if min_val > max_val {\n        return CurveParams { shift: 0, bits: 1 };\n    }\n    let shift: u32 = if min_val < 0 {\n        min_val.unsigned_abs()\n    } else {\n        0\n    };\n    // extent = largest shifted coordinate value that any vertex can take.\n    let extent = (i64::from(max_val) + i64::from(shift)).unsigned_abs();\n    // bits = ceil(log2(extent + 1)), i.e. the smallest l s.t. 2^l > extent.\n    // For extent = 0 the grid degenerates to a single cell; use level 1.\n    let bits = if extent == 0 {\n        1\n    } else {\n        (u64::BITS - extent.leading_zeros()).min(16)\n    };\n\n    CurveParams { shift, bits }\n}\n\n// ─── Tests ───────────────────────────────────────────────────────────────────\n\n#[cfg(test)]\nmod tests {\n\n    use super::*;\n    use crate::codecs::morton::interleave_bits;\n\n    const fn c(x: i32, y: i32) -> Coord<i32> {\n        Coord::<i32> { x, y }\n    }\n\n    const fn p(shift: u32, bits: u32) -> CurveParams {\n        CurveParams { shift, bits }\n    }\n\n    /// Return the `(x, y)` coordinates for Hilbert curve index `pos` at `level`.\n    ///\n    /// This is the inverse of [`hilbert_xy_to_index`]: the returned coordinates\n    /// are in `[0, 2^level)`.  `level` must be in `[1, 16]` and `pos` must be\n    /// in `[0, 4^level)`.\n    fn hilbert_position_to_xy(level: u32, pos: u32) -> Coord<u32> {\n        debug_assert!((1..=16).contains(&level), \"level must be in [1, 16]\");\n        debug_assert!(u64::from(pos) < (1u64 << (2 * level)), \"pos out of range\");\n        hilbert_2d::u32::h2xy_discrete(pos, level, hilbert_2d::Variant::Hilbert).into()\n    }\n\n    #[test]\n    fn hilbert_origin_always_zero() {\n        // The origin maps to index 0 at every level.\n        for level in 1u32..=8 {\n            let idx = hilbert_xy_to_index(level, (0, 0).into());\n            assert_eq!(idx, 0, \"origin should be 0 at level {level}\");\n        }\n    }\n\n    #[test]\n    fn hilbert_round_trip_level1() {\n        // 2×2 grid: all four cells must encode then decode back to themselves.\n        for x in 0u32..2 {\n            for y in 0u32..2 {\n                let idx = hilbert_xy_to_index(1, (x, y).into());\n                let (rx, ry) = hilbert_position_to_xy(1, idx).into();\n                assert_eq!((rx, ry), (x, y), \"round-trip failed at level=1 ({x},{y})\");\n            }\n        }\n    }\n\n    #[test]\n    fn hilbert_round_trip_level2() {\n        // 4×4 grid: all 16 cells.\n        for x in 0u32..4 {\n            for y in 0u32..4 {\n                let idx = hilbert_xy_to_index(2, (x, y).into());\n                let (rx, ry) = hilbert_position_to_xy(2, idx).into();\n                assert_eq!((rx, ry), (x, y), \"round-trip failed at level=2 ({x},{y})\");\n            }\n        }\n    }\n\n    #[test]\n    fn hilbert_round_trip_level4() {\n        // 16×16 grid: all 256 cells.\n        for x in 0u32..16 {\n            for y in 0u32..16 {\n                let idx = hilbert_xy_to_index(4, (x, y).into());\n                let (rx, ry) = hilbert_position_to_xy(4, idx).into();\n                assert_eq!((rx, ry), (x, y), \"round-trip failed at level=4 ({x},{y})\");\n            }\n        }\n    }\n\n    #[test]\n    fn hilbert_indices_are_a_bijection_at_level2() {\n        // Every index in [0, 16) must appear exactly once.\n        let mut seen = [false; 16];\n        for x in 0u32..4 {\n            for y in 0u32..4 {\n                let idx = hilbert_xy_to_index(2, (x, y).into()) as usize;\n                assert!(!seen[idx], \"duplicate index {idx} at ({x},{y})\");\n                seen[idx] = true;\n            }\n        }\n        assert!(seen.iter().all(|&v| v), \"some index was never produced\");\n    }\n\n    #[test]\n    fn hilbert_indices_are_a_bijection_at_level4() {\n        let mut seen = vec![false; 256];\n        for x in 0u32..16 {\n            for y in 0u32..16 {\n                let idx = hilbert_xy_to_index(4, (x, y).into()) as usize;\n                assert!(!seen[idx], \"duplicate index {idx} at ({x},{y})\");\n                seen[idx] = true;\n            }\n        }\n        assert!(seen.iter().all(|&v| v));\n    }\n\n    #[test]\n    fn hilbert_level1_covers_indices_0_to_3() {\n        // Collect all indices produced for the 2×2 grid.\n        let mut indices: Vec<u32> = (0u32..2)\n            .flat_map(|x| (0u32..2).map(move |y| hilbert_xy_to_index(1, (x, y).into())))\n            .collect();\n        indices.sort_unstable();\n        assert_eq!(indices, [0, 1, 2, 3]);\n    }\n\n    // ── hilbert_sort_key ──────────────────────────────────────────────────────\n\n    #[test]\n    fn hilbert_sort_key_origin_zero() {\n        assert_eq!(hilbert_sort_key(c(0, 0), p(0, 1)), 0);\n    }\n\n    #[test]\n    fn hilbert_sort_key_negative_coords_shift_correctly() {\n        // (-1, -1) shifted by 1 maps to (0, 0) -> Hilbert index 0 at any level.\n        assert_eq!(hilbert_sort_key(c(-1, -1), p(1, 1)), 0);\n    }\n\n    #[test]\n    fn hilbert_sort_key_matches_xy_to_index() {\n        // hilbert_sort_key should agree with a direct call to hilbert_xy_to_index\n        // after shifting.\n        let shift = 5u32;\n        let bits = 4u32;\n        for raw_x in -5i32..11 {\n            for raw_y in -5i32..11 {\n                let expected = hilbert_xy_to_index(\n                    bits,\n                    (\n                        u32::try_from(i64::from(raw_x) + i64::from(shift)).unwrap(),\n                        u32::try_from(i64::from(raw_y) + i64::from(shift)).unwrap(),\n                    )\n                        .into(),\n                );\n                let actual = hilbert_sort_key(c(raw_x, raw_y), p(shift, bits));\n                assert_eq!(actual, expected, \"mismatch at ({raw_x},{raw_y})\");\n            }\n        }\n    }\n\n    #[test]\n    fn curve_params_empty_bounds() {\n        // min > max signals empty input.\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(i32::MAX, i32::MIN);\n        assert_eq!(shift, 0);\n        assert_eq!(bits, 1);\n    }\n\n    #[test]\n    fn curve_params_all_zero() {\n        // Degenerate: single point at origin, extent = 0 -> level 1.\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(0, 0);\n        assert_eq!(shift, 0);\n        assert_eq!(bits, 1);\n    }\n\n    #[test]\n    fn curve_params_positive_only() {\n        // Bounds [0, 3]: shift = 0, bits = 2 (2^2=4 > 3).\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(0, 3);\n        assert_eq!(shift, 0);\n        assert_eq!(bits, 2);\n    }\n\n    #[test]\n    fn curve_params_negative_min() {\n        // Bounds [-4, 4]: shift = 4, extent = 4+4 = 8, bits = 4 (2^4=16 > 8).\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(-4, 4);\n        assert_eq!(shift, 4);\n        assert_eq!(bits, 4);\n    }\n\n    #[test]\n    fn curve_params_power_of_two_extent() {\n        // extent = 8 = 2^3: need level 4 so that 2^4 = 16 > 8.\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(0, 8);\n        assert_eq!(shift, 0);\n        assert_eq!(bits, 4);\n    }\n\n    #[test]\n    fn curve_params_single_axis_negative() {\n        // Global min = -2 -> shift = 2; extent = 5 + 2 = 7; bits = 3.\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(-2, 5);\n        assert_eq!(shift, 2);\n        assert_eq!(bits, 3);\n    }\n\n    #[test]\n    fn curve_params_clamped_at_16_bits() {\n        // extent = 65535 = 2^16 - 1, bits = 16.\n        let CurveParams { shift, bits } = hilbert_curve_params_from_bounds(0, 65535);\n        assert_eq!(shift, 0);\n        assert_eq!(bits, 16);\n    }\n\n    // ── Hilbert vs Morton locality comparison ─────────────────────────────────\n\n    #[test]\n    fn hilbert_and_morton_both_sort_nearby_points_close_together() {\n        // A dense cluster of points in [0, 7]×[0, 7] should produce\n        // keys that are all close to each other under both curves.\n        // We verify that the maximum key spread for a 2×2 neighbourhood is\n        // smaller than the total key range for the full 8×8 grid.\n        let points: Vec<Coord<u32>> = (0u32..8)\n            .flat_map(|x| (0u32..8).map(move |y| (x, y).into()))\n            .collect();\n\n        // Hilbert: level = 3 (2^3 = 8)\n        let h_keys: Vec<u32> = points.iter().map(|&c| hilbert_xy_to_index(3, c)).collect();\n        let h_max = h_keys.iter().copied().max().unwrap();\n\n        // Morton\n        let m_keys: Vec<u32> = points.iter().map(|&c| interleave_bits(c)).collect();\n        let m_max = m_keys.iter().copied().max().unwrap();\n\n        // Both should cover the full range for the grid.\n        assert_eq!(h_max, 63, \"Hilbert should produce indices 0..63 for 8×8\");\n        assert_eq!(m_max, interleave_bits((7, 7).into()));\n\n        // Neither curve should map (0,0) and (1,0) more than 4 steps apart at level 3.\n        let h_00 = hilbert_xy_to_index(3, (0, 0).into());\n        let h_10 = hilbert_xy_to_index(3, (1, 0).into());\n        assert!(\n            h_00.abs_diff(h_10) <= 4,\n            \"adjacent points should have close Hilbert indices\"\n        );\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/mod.rs",
    "content": "pub mod bytes;\npub mod fastpfor;\npub mod fsst;\npub mod hilbert;\npub mod morton;\npub mod rle;\npub mod varint;\npub mod zigzag;\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/morton.rs",
    "content": "use geo_types::Coord;\nuse wide::u32x8;\n\nuse crate::decoder::Morton;\nuse crate::encoder::model::CurveParams;\nuse crate::{Decoder, MltError, MltResult};\n\nconst LANES: usize = 8;\n\n// ── Bit interleaving ─────────────────────────────────────────────────────────\n\n/// Interleave the lower 16 bits of `x` and `y` into a 32-bit Morton code.\n///\n/// Even bit positions (0, 2, 4, …) encode `x`; odd positions (1, 3, 5, …)\n/// encode `y`. Spatially adjacent `(x, y)` pairs produce numerically\n/// adjacent codes, giving Z-order locality when used as a sort key.\n#[must_use]\n#[inline]\npub fn interleave_bits(coord: Coord<u32>) -> u32 {\n    // Spread each input's lower 16 bits into every other bit position, then\n    // OR the two together: x occupies even positions (0, 2, 4, …) and y\n    // occupies odd positions (1, 3, 5, …).\n    let mut sx = coord.x & 0xFFFF;\n    sx = (sx | (sx << 8)) & 0x00FF_00FF;\n    sx = (sx | (sx << 4)) & 0x0F0F_0F0F;\n    sx = (sx | (sx << 2)) & 0x3333_3333;\n    sx = (sx | (sx << 1)) & 0x5555_5555;\n\n    let mut sy = coord.y & 0xFFFF;\n    sy = (sy | (sy << 8)) & 0x00FF_00FF;\n    sy = (sy | (sy << 4)) & 0x0F0F_0F0F;\n    sy = (sy | (sy << 2)) & 0x3333_3333;\n    sy = (sy | (sy << 1)) & 0x5555_5555;\n\n    sx | (sy << 1)\n}\n\n/// Compute a Z-order (Morton) sort key from signed integer coordinates.\n///\n/// `shift` is applied to both axes before bit-interleaving to move the\n/// coordinate origin into the non-negative range. It should be computed\n/// once across the entire feature set (typically `min.unsigned_abs()` when\n/// `min < 0`, else `0`) so that the keys are comparable across features.\n///\n/// Each shifted component is truncated to 16 bits before interleaving, so\n/// the returned key fits in a `u32` (32 interleaved bits). This is\n/// sufficient for any tile coordinate system with extent ≤ 65 535.\n#[must_use]\n#[inline]\npub fn morton_sort_key(c: Coord<i32>, params: CurveParams) -> u32 {\n    debug_assert!((1..=16).contains(&params.bits));\n    #[expect(\n        clippy::cast_possible_truncation,\n        clippy::cast_sign_loss,\n        reason = \"shift brings value into [0, extent]; masked to 16 bits immediately after\"\n    )]\n    let sx = ((i64::from(c.x) + i64::from(params.shift)) as u32) & 0xFFFF;\n    #[expect(\n        clippy::cast_possible_truncation,\n        clippy::cast_sign_loss,\n        reason = \"shift brings value into [0, extent]; masked to 16 bits immediately after\"\n    )]\n    let sy = ((i64::from(c.y) + i64::from(params.shift)) as u32) & 0xFFFF;\n    interleave_bits((sx, sy).into())\n}\n\n// ── Encoder ─────────────────────────────────────────────────────────────────\nimpl Morton {\n    /// Compute `ZOrderCurve` parameters from the vertex value range.\n    ///\n    /// Returns a [`Morton`] whose `bits` and `shift` match Java's\n    /// `SpaceFillingCurve` implementation.\n    pub fn from_vertices(vertices: &[i32]) -> MltResult<Self> {\n        let min_v = vertices.iter().copied().min().unwrap_or(0);\n        let max_v = vertices.iter().copied().max().unwrap_or(0);\n        let shift: u32 = if min_v < 0 { min_v.unsigned_abs() } else { 0 };\n        let tile_extent = i64::from(max_v) + i64::from(shift);\n        let bits = if let Ok(extent) = u32::try_from(tile_extent) {\n            // ceil(log2(extent + 1)), matching Java's Math.ceil(Math.log(...) / Math.log(2)).\n            // Computed with integer arithmetic: for te >= 1, this equals `u32::BITS - te.leading_zeros()`.\n            // Capped at 16: Morton codes are u32, so each axis may use at most 16 bits.\n            let required_bits = u32::BITS - extent.leading_zeros();\n            if required_bits > 16 {\n                return Err(MltError::VertexMortonNotCompatibleWithExtent {\n                    extent,\n                    required_bits,\n                });\n            }\n            required_bits\n        } else {\n            0u32\n        };\n        Self::new(bits, shift)\n    }\n\n    /// Encode a single `(x, y)` coordinate pair to its Z-order (Morton) code.\n    ///\n    /// `bits` (≤ 16) bits are used per axis; `shift` is added to each\n    /// component before interleaving so that negative coordinates map to non-negative values.\n    #[inline]\n    pub fn encode_morton(self, x: i32, y: i32) -> MltResult<u32> {\n        let sx = u32::try_from(i64::from(x) + i64::from(self.shift))?;\n        let sy = u32::try_from(i64::from(y) + i64::from(self.shift))?;\n        let mut code = 0u32;\n        for i in 0..self.bits {\n            // bits are capped at 16, so 2*i+1 ≤ 31 — no shift overflow.\n            code |= ((sx >> i) & 1) << (2 * i);\n            code |= ((sy >> i) & 1) << (2 * i + 1);\n        }\n        Ok(code)\n    }\n}\n\nimpl Morton {\n    /// Decode a single Morton code to a `Coord<i32>`, applying `shift`.\n    #[inline]\n    fn decode_one(self, morton_code: u32) -> Coord<i32> {\n        let mut x = 0u32;\n        let mut y = 0u32;\n        for i in 0..self.bits {\n            let bit_mask = 1u32 << (2 * i);\n            x |= (morton_code & bit_mask) >> i;\n            y |= ((morton_code >> 1) & bit_mask) >> i;\n        }\n        Coord::<i32> {\n            x: x.wrapping_sub(self.shift).cast_signed(),\n            y: y.wrapping_sub(self.shift).cast_signed(),\n        }\n    }\n\n    /// Decode Morton codes (no delta) to flat `[x0, y0, x1, y1, ...]`, charging `dec` for the output.\n    ///\n    /// Processes 8 codes at a time with `wide::u32x8`. Each lane extracts the\n    /// compacted even-bit (x) and odd-bit (y) components in parallel, then applies\n    /// the coordinate shift. A scalar tail handles any remaining codes.\n    pub fn decode_codes(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {\n        let alloc_size = data.len() * 2;\n        let mut out = dec.alloc(alloc_size)?;\n        let shift_vec = u32x8::splat(self.shift);\n\n        let mut chunks = data.chunks_exact(LANES);\n\n        for chunk in chunks.by_ref() {\n            let buf = [\n                chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],\n            ];\n            self.decode_chunk(buf, shift_vec, &mut out);\n        }\n\n        // Scalar tail for any codes that didn't fill a full SIMD chunk.\n        for &code in chunks.remainder() {\n            let coord = self.decode_one(code);\n            out.push(coord.x);\n            out.push(coord.y);\n        }\n\n        dec.adjust_alloc(&out, alloc_size)?;\n        Ok(out)\n    }\n\n    /// Decode delta-encoded Morton codes to flat `[x0, y0, x1, y1, ...]`, charging `dec` for the output.\n    ///\n    /// Each input value is a signed delta (stored as u32 with wrapping arithmetic)\n    /// relative to the previous Morton code. The sequential prefix sum is computed\n    /// in chunks of 8 into a stack-allocated buffer, which is then SIMD-decoded.\n    /// This keeps the working set in registers / L1 cache.\n    pub fn decode_delta(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {\n        let alloc_size = data.len() * 2;\n        let mut out = dec.alloc(alloc_size)?;\n        let shift_vec = u32x8::splat(self.shift);\n\n        let mut prev = 0i32;\n        let mut chunks = data.chunks_exact(LANES);\n\n        for chunk in chunks.by_ref() {\n            // Sequential prefix sum into a stack buffer — no heap allocation.\n            let mut buf = [0u32; LANES];\n            for (b, &d) in buf.iter_mut().zip(chunk.iter()) {\n                prev = prev.wrapping_add(d.cast_signed());\n                *b = prev.cast_unsigned();\n            }\n            self.decode_chunk(buf, shift_vec, &mut out);\n        }\n\n        // Scalar tail for any codes that didn't fill a full SIMD chunk.\n        for &d in chunks.remainder() {\n            prev = prev.wrapping_add(d.cast_signed());\n            let coord = self.decode_one(prev.cast_unsigned());\n            out.push(coord.x);\n            out.push(coord.y);\n        }\n\n        dec.adjust_alloc(&out, alloc_size)?;\n        Ok(out)\n    }\n\n    /// SIMD-decode a chunk of exactly 8 resolved Morton codes into the output buffer.\n    ///\n    /// Each code has already been resolved to its absolute value (no delta pending).\n    /// Even-indexed bits encode x, odd-indexed bits encode y.\n    #[inline]\n    fn decode_chunk(self, buf: [u32; LANES], shift_vec: u32x8, out: &mut Vec<i32>) {\n        let codes = u32x8::from(buf);\n        // Odd bits become even after shifting right by 1, giving the y component.\n        let codes_y = codes >> 1;\n\n        let mut x_vec = u32x8::ZERO;\n        let mut y_vec = u32x8::ZERO;\n\n        for i in 0..self.bits {\n            // Mask for the bit position 2*i in the original Morton code.\n            let bit_mask = u32x8::splat(1u32 << (2 * i));\n            // Extract bit 2*i from each code and shift it down to position i.\n            x_vec |= (codes & bit_mask) >> i;\n            y_vec |= (codes_y & bit_mask) >> i;\n        }\n\n        let xs: [u32; LANES] = (x_vec - shift_vec).into();\n        let ys: [u32; LANES] = (y_vec - shift_vec).into();\n\n        for lane in 0..LANES {\n            out.push(xs[lane].cast_signed());\n            out.push(ys[lane].cast_signed());\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use super::*;\n    use crate::test_helpers::dec;\n\n    const fn c(x: i32, y: i32) -> Coord<i32> {\n        Coord::<i32> { x, y }\n    }\n\n    const fn p(shift: u32, bits: u32) -> CurveParams {\n        CurveParams { shift, bits }\n    }\n\n    // ── interleave_bits / morton_sort_key ─────────────────────────────────────\n\n    /// Spread the lower 16 bits of `tx` into the even bit positions (0, 2, 4, …)\n    /// of a 32-bit word, inserting a 0 between every original bit.\n    fn spread_bits(mut tx: u32) -> u32 {\n        tx = (tx | (tx << 8)) & 0x00FF_00FF;\n        tx = (tx | (tx << 4)) & 0x0F0F_0F0F;\n        tx = (tx | (tx << 2)) & 0x3333_3333;\n        tx = (tx | (tx << 1)) & 0x5555_5555;\n        tx\n    }\n\n    /// Compact the bits at even positions (0, 2, 4, …) of `tx` into the lower\n    /// 16 bits, discarding the interleaved zeros.\n    fn compact_bits(mut tx: u32) -> u32 {\n        tx &= 0x5555_5555;\n        tx = (tx | (tx >> 1)) & 0x3333_3333;\n        tx = (tx | (tx >> 2)) & 0x0F0F_0F0F;\n        tx = (tx | (tx >> 4)) & 0x00FF_00FF;\n        tx = (tx | (tx >> 8)) & 0x0000_FFFF;\n        tx\n    }\n\n    #[test]\n    fn spread_then_compact_is_identity() {\n        for x in 0u32..=0xFFFF {\n            assert_eq!(compact_bits(spread_bits(x)), x, \"round-trip failed for {x}\");\n        }\n    }\n\n    #[test]\n    fn spread_bits_places_bit0_at_position0() {\n        assert_eq!(spread_bits(1), 1);\n    }\n\n    #[test]\n    fn spread_bits_places_bit1_at_position2() {\n        assert_eq!(spread_bits(2), 4);\n    }\n\n    #[test]\n    fn spread_bits_places_bit2_at_position4() {\n        assert_eq!(spread_bits(4), 16);\n    }\n\n    #[test]\n    fn origin_maps_to_zero() {\n        assert_eq!(morton_sort_key(c(0, 0), p(0, 16)), 0);\n    }\n\n    #[test]\n    fn x_axis_produces_even_bits() {\n        // x=1, y=0  →  only bit 0 of x is set → Morton bit 0 set → code = 1\n        assert_eq!(morton_sort_key(c(1, 0), p(0, 16)), 1);\n        // x=2, y=0  →  only bit 1 of x is set → Morton bit 2 set → code = 4\n        assert_eq!(morton_sort_key(c(2, 0), p(0, 16)), 4);\n    }\n\n    #[test]\n    fn y_axis_produces_odd_bits() {\n        // x=0, y=1  →  only bit 0 of y is set → Morton bit 1 set → code = 2\n        assert_eq!(morton_sort_key(c(0, 1), p(0, 16)), 2);\n        // x=0, y=2  →  only bit 1 of y is set → Morton bit 3 set → code = 8\n        assert_eq!(morton_sort_key(c(0, 2), p(0, 16)), 8);\n    }\n\n    #[test]\n    fn negative_coords_shift_correctly() {\n        // Shifting (-1, -1) by 1 maps to (0, 0) → Morton code 0\n        assert_eq!(morton_sort_key(c(-1, -1), p(1, 16)), 0);\n        // Shifting (-1, 0) by 1 maps to (0, 1) → Morton code 2\n        assert_eq!(morton_sort_key(c(-1, 0), p(1, 16)), 2);\n    }\n\n    #[test]\n    fn spatial_locality_z_order() {\n        // After shifting, (0,0) < (1,0) < (0,1) < (1,1) in Z-order\n        let k00 = morton_sort_key(c(0, 0), p(0, 16));\n        let k10 = morton_sort_key(c(1, 0), p(0, 16));\n        let k01 = morton_sort_key(c(0, 1), p(0, 16));\n        let k11 = morton_sort_key(c(1, 1), p(0, 16));\n        assert!(k00 < k10);\n        assert!(k10 < k01);\n        assert!(k01 < k11);\n    }\n\n    #[test]\n    fn interleave_round_trips_via_deinterleave() {\n        // Reconstruct x and y from interleaved bits and verify round-trip.\n        for x in 0u32..16 {\n            for y in 0u32..16 {\n                let code = interleave_bits((x, y).into());\n                let mut rx = 0u32;\n                let mut ry = 0u32;\n                for bit in 0..16 {\n                    rx |= ((code >> (2 * bit)) & 1) << bit;\n                    ry |= ((code >> (2 * bit + 1)) & 1) << bit;\n                }\n                assert_eq!(rx, x, \"x mismatch for ({x}, {y})\");\n                assert_eq!(ry, y, \"y mismatch for ({x}, {y})\");\n            }\n        }\n    }\n\n    // ── Morton encode/decode tests ────────────────────────────────────────────\n\n    const NUM_BITS: u32 = 15;\n    const COORD_SHIFT: u32 = 1 << (NUM_BITS - 1); // 16384\n    const MORTON: Morton = Morton {\n        bits: NUM_BITS,\n        shift: COORD_SHIFT,\n    };\n\n    /// Interleave `x` and `y` into a single Morton code using 15 bits per component.\n    ///\n    /// Even bit positions encode `x`, odd positions encode `y`.\n    /// This is the inverse of [`Morton::decode_codes`] / [`Morton::decode_delta`].\n    #[must_use]\n    #[inline]\n    pub fn encode_morton_15(coord: Coord<u32>) -> u32 {\n        let mut code = 0u32;\n        for bit in 0..15 {\n            code |= ((coord.x >> bit) & 1) << (2 * bit);\n            code |= ((coord.y >> bit) & 1) << (2 * bit + 1);\n        }\n        code\n    }\n\n    #[test]\n    fn test_decode_morton_codes_empty() {\n        assert!(MORTON.decode_codes(&[], &mut dec()).unwrap().is_empty());\n    }\n\n    #[test]\n    fn test_decode_morton_codes_origin() {\n        // Morton code for (COORD_SHIFT, COORD_SHIFT) should decode to (0, 0).\n        let code = encode_morton_15((COORD_SHIFT, COORD_SHIFT).into());\n        let decoded = MORTON.decode_codes(&[code], &mut dec()).unwrap();\n        assert_eq!(decoded, [0, 0]);\n    }\n\n    #[test]\n    fn test_decode_morton_codes_known_values() {\n        // x=1, y=2 (pre-shift) → decoded (1 - COORD_SHIFT, 2 - COORD_SHIFT)\n        let x: u32 = 1;\n        let y: u32 = 2;\n        let code = encode_morton_15((x, y).into());\n        let expected_x = x.cast_signed() - COORD_SHIFT.cast_signed();\n        let expected_y = y.cast_signed() - COORD_SHIFT.cast_signed();\n        let decoded = MORTON.decode_codes(&[code], &mut dec()).unwrap();\n        assert_eq!(decoded, [expected_x, expected_y]);\n    }\n\n    #[test]\n    fn test_decode_morton_codes_scalar_tail() {\n        // 3 codes — exercises the scalar tail path (< 8 codes).\n        let pairs: [Coord<u32>; _] = [(0, 1).into(), (2, 3).into(), (4, 5).into()];\n        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();\n        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();\n        let expected = expected_coords(&pairs);\n        assert_eq!(result, expected);\n    }\n\n    #[test]\n    fn test_decode_morton_codes_full_simd_chunk() {\n        // 8 codes — exercises exactly one SIMD chunk, no scalar tail.\n        let pairs: [Coord<u32>; _] = [\n            (0, 0).into(),\n            (1, 0).into(),\n            (0, 1).into(),\n            (1, 1).into(),\n            (2, 3).into(),\n            (7, 5).into(),\n            (10, 9).into(),\n            (15, 15).into(),\n        ];\n        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();\n        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();\n        let expected = expected_coords(&pairs);\n        assert_eq!(result, expected);\n    }\n\n    #[test]\n    fn test_decode_morton_codes_simd_plus_tail() {\n        // 11 codes — one full SIMD chunk of 8 plus a scalar tail of 3.\n        let pairs: Vec<Coord<u32>> = (0..11u32)\n            .map(|i| (i * 3 % 100, i * 7 % 100).into())\n            .collect();\n        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();\n        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();\n        let expected = expected_coords(&pairs);\n        assert_eq!(result, expected);\n    }\n\n    // --- decode_delta tests ---\n\n    #[test]\n    fn test_decode_morton_delta_empty() {\n        assert!(MORTON.decode_delta(&[], &mut dec()).unwrap().is_empty());\n    }\n\n    #[test]\n    fn test_decode_morton_delta_identity_with_zero_deltas() {\n        // All-zero deltas: every resolved code is 0, which decodes to (-COORD_SHIFT, -COORD_SHIFT).\n        let deltas = vec![0u32; 3];\n        let result = MORTON.decode_delta(&deltas, &mut dec()).unwrap();\n        let shift = -COORD_SHIFT.cast_signed();\n        assert_eq!(result, vec![shift, shift, shift, shift, shift, shift]);\n    }\n\n    #[test]\n    fn test_decode_morton_delta_matches_codes_after_prefix_sum() {\n        // Build a sequence of absolute codes, compute their deltas, then verify that\n        // decode_delta produces the same output as decode_codes on the original absolute codes.\n        let pairs: Vec<Coord<u32>> = (0..11u32)\n            .map(|i| (i * 5 % 200, i * 9 % 200).into())\n            .collect();\n        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();\n        let deltas = signed_deltas(&codes);\n\n        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();\n        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();\n        assert_eq!(from_codes, from_deltas);\n    }\n\n    #[test]\n    fn test_decode_morton_delta_scalar_tail() {\n        // 3 codes via deltas — scalar tail path only.\n        let codes: Vec<u32> = vec![\n            encode_morton_15((10, 20).into()),\n            encode_morton_15((30, 40).into()),\n            encode_morton_15((50, 60).into()),\n        ];\n        let deltas = signed_deltas(&codes);\n        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();\n        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();\n        assert_eq!(from_codes, from_deltas);\n    }\n\n    #[test]\n    fn test_decode_morton_delta_wrapping() {\n        // A single wrapping delta: start from a large code, subtract more than it — should\n        // still round-trip correctly via wrapping arithmetic.\n        let code_a = encode_morton_15((500, 300).into());\n        let code_b = encode_morton_15((10, 10).into()); // numerically smaller than code_a\n        let delta_b = code_b\n            .cast_signed()\n            .wrapping_sub(code_a.cast_signed())\n            .cast_unsigned();\n        assert_eq!(\n            MORTON.decode_delta(&[code_a, delta_b], &mut dec()).unwrap(),\n            MORTON.decode_codes(&[code_a, code_b], &mut dec()).unwrap()\n        );\n    }\n\n    /// Compute expected decoded `[x0, y0, x1, y1, ...]` from raw (pre-shift) coordinate pairs.\n    fn expected_coords(pairs: &[Coord<u32>]) -> Vec<i32> {\n        pairs\n            .iter()\n            .flat_map(|&Coord { x, y }| {\n                [\n                    x.cast_signed() - COORD_SHIFT.cast_signed(),\n                    y.cast_signed() - COORD_SHIFT.cast_signed(),\n                ]\n            })\n            .collect()\n    }\n\n    /// Compute wrapping signed deltas between consecutive Morton codes.\n    fn signed_deltas(codes: &[u32]) -> Vec<u32> {\n        let mut prev = 0i32;\n        codes\n            .iter()\n            .map(|&c| {\n                let delta = c.cast_signed().wrapping_sub(prev).cast_unsigned();\n                prev = c.cast_signed();\n                delta\n            })\n            .collect()\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/rle.rs",
    "content": "use num_traits::PrimInt;\n\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltError, MltResult};\n\n/// Generic run-length encode: returns `(run_lengths, values)`.\n#[must_use]\npub fn encode_rle<T: PrimInt>(data: &[T]) -> (Vec<T>, Vec<T>) {\n    if data.is_empty() {\n        return (Vec::new(), Vec::new());\n    }\n\n    let mut runs = Vec::new();\n    let mut values = Vec::new();\n\n    let mut current_val = data[0];\n    let mut current_run = T::one();\n\n    for &val in &data[1..] {\n        if val == current_val {\n            current_run = current_run.saturating_add(T::one());\n        } else {\n            runs.push(current_run);\n            values.push(current_val);\n            current_val = val;\n            current_run = T::one();\n        }\n    }\n    runs.push(current_run);\n    values.push(current_val);\n\n    (runs, values)\n}\n\n/// Encode byte-level RLE as used in ORC for boolean and present streams.\n///\n/// Format: control byte determines the run type:\n/// - `control >= 128`: literal run of `(256 - control)` bytes follow\n/// - `control < 128`: repeating run of `(control + 3)` copies of the next byte\npub fn encode_byte_rle<'a>(data: &[u8], target: &'a mut Vec<u8>) -> &'a [u8] {\n    target.clear();\n    let mut pos = 0;\n\n    while pos < data.len() {\n        // Look ahead for repeating run\n        let mut repeat_count = 1;\n        while pos + repeat_count < data.len()\n            && data[pos + repeat_count] == data[pos]\n            && repeat_count < 130\n        {\n            repeat_count += 1;\n        }\n\n        if repeat_count >= 3 {\n            // Encode repeating run\n            #[expect(clippy::cast_possible_truncation, reason = \"3 <= repeat_count < 130\")]\n            let control = (repeat_count - 3) as u8;\n            target.push(control);\n            target.push(data[pos]);\n            pos += repeat_count;\n        } else {\n            // Encode literal run\n            let mut literal_count = 0;\n            // Scan ahead to see where the next repeating run starts\n            while pos + literal_count < data.len() && literal_count < 128 {\n                let mut inner_repeat = 1;\n                while let next_idx = pos + literal_count\n                    && next_idx + inner_repeat < data.len()\n                    && data[next_idx + inner_repeat] == data[next_idx]\n                    && inner_repeat < 3\n                {\n                    inner_repeat += 1;\n                }\n\n                if inner_repeat >= 3 {\n                    break;\n                }\n                literal_count += 1;\n            }\n\n            #[expect(\n                clippy::cast_possible_truncation,\n                reason = \"literal_count is always smaller than 128\"\n            )]\n            let control = (256 - literal_count) as u8;\n            target.push(control);\n            target.extend_from_slice(&data[pos..pos + literal_count]);\n            pos += literal_count;\n        }\n    }\n    target\n}\n\n/// Decode byte-level RLE as used in ORC for boolean and present streams.\n///\n/// Format: control byte determines the run type:\n/// - `control >= 128`: literal run of `(256 - control)` bytes follow\n/// - `control < 128`: repeating run of `(control + 3)` copies of the next byte\npub fn decode_byte_rle(input: &[u8], num_bytes: usize, dec: &mut Decoder) -> MltResult<Vec<u8>> {\n    let mut output = dec.alloc(num_bytes)?;\n    let mut pos = 0;\n    while output.len() < num_bytes && pos < input.len() {\n        let control = input[pos];\n        pos += 1;\n        if control >= 128 {\n            let count = u32::from(control ^ 0xFF) + 1;\n            let end = pos + count.as_usize();\n            let slice = input.get(pos..end).ok_or(MltError::BufferUnderflow(\n                count,\n                input.len().saturating_sub(pos),\n            ))?;\n            output.extend_from_slice(slice);\n            pos = end;\n        } else {\n            let count = usize::from(control) + 3;\n            let &value = input.get(pos).ok_or(MltError::BufferUnderflow(1, 0))?;\n            pos += 1;\n            output.extend(std::iter::repeat_n(value, count));\n        }\n    }\n    dec.adjust_alloc(&output, num_bytes)?;\n    Ok(output)\n}\n\n#[cfg(test)]\nmod tests {\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::decoder::RleMeta;\n    use crate::test_helpers::dec;\n\n    proptest! {\n        #[test]\n        fn test_rle_roundtrip_u32(data: Vec<u32>) {\n            let (runs, vals) = encode_rle(&data);\n            let mut combined = runs.clone();\n            combined.extend(vals);\n            let runs = u32::try_from(runs.len()).unwrap();\n            let num_rle_values = u32::try_from(data.len()).unwrap();\n            let rle = RleMeta { runs, num_rle_values };\n            let decoded = rle.decode(&combined, &mut dec()).unwrap();\n            prop_assert_eq!(data, decoded);\n        }\n\n        #[test]\n        fn test_byte_rle_roundtrip(data: Vec<u8>) {\n            let mut encoded = Vec::new();\n            let buf = encode_byte_rle(&data, &mut encoded);\n            let decoded = decode_byte_rle(buf, data.len(), &mut dec()).unwrap();\n            prop_assert_eq!(data, decoded);\n        }\n    }\n\n    #[test]\n    fn test_encode_rle_empty() {\n        let (runs, vals) = encode_rle::<u8>(&[]);\n        assert!(runs.is_empty());\n        assert!(vals.is_empty());\n    }\n\n    #[test]\n    fn test_encode_byte_rle_empty() {\n        let mut buf = Vec::new();\n        assert!(encode_byte_rle(&[], &mut buf).is_empty());\n    }\n\n    #[test]\n    fn test_decode_byte_rle_empty() {\n        assert!(decode_byte_rle(&[], 0, &mut dec()).unwrap().is_empty());\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/varint.rs",
    "content": "use integer_encoding::VarInt;\n\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltError, MltRefResult};\n\n/// Parse a single varint (variable-length integer) from the input, returning\n/// the remaining bytes and the decoded value.\n///\n/// Validates canonical encoding: a multibyte varint must not have a trailing\n/// zero byte (which would mean it could have been encoded in fewer bytes).\n#[inline]\npub fn parse_varint<T: VarInt>(input: &[u8]) -> MltRefResult<'_, T> {\n    match T::decode_var(input) {\n        Some((value, consumed)) => {\n            // A varint is canonical if its last byte is non-zero (for multibyte encodings).\n            // Value 0 must be encoded as a single 0x00 byte.\n            // For multibyte VarInts, the last byte (without a continuation bit) must be non-zero.\n            // Using more bytes than necessary violates roundtrip-ability of MLT.\n            if consumed > 1 && input[consumed - 1] == 0 {\n                return Err(MltError::NonCanonicalVarInt);\n            }\n            Ok((&input[consumed..], value))\n        }\n        None => Err(MltError::BufferUnderflow(\n            u32::try_from(input.len().saturating_add(1))?,\n            input.len(),\n        )),\n    }\n}\n\n/// Parse `size` varints of wire type `T` into a `Vec<U>`, charging `dec` for\n/// the output allocation.\npub fn parse_varint_vec<'a, T, U>(\n    mut input: &'a [u8],\n    size: u32,\n    dec: &mut Decoder,\n) -> MltRefResult<'a, Vec<U>>\nwhere\n    T: VarInt,\n    U: TryFrom<T>,\n    MltError: From<<U as TryFrom<T>>::Error>,\n{\n    let mut values = dec.alloc::<U>(size.as_usize())?;\n    let mut val;\n    for _ in 0..size {\n        (input, val) = parse_varint::<T>(input)?;\n        values.push(val.try_into()?);\n    }\n    Ok((input, values))\n}\n\n#[cfg(test)]\nmod tests {\n    use rstest::rstest;\n\n    use super::*;\n    use crate::MltResult;\n    use crate::test_helpers::dec;\n\n    #[rstest]\n    #[case::trailing_bytes(&[0x80, 0x01, 0x42], Ok((vec![0x42_u8], 128)))]\n    #[case::zero(&[0x00], Ok((vec![], 0)))]\n    #[case::max_single_byte(&[0x7F], Ok((vec![], 127)))]\n    #[case::min_two_byte(&[0x80, 0x01], Ok((vec![], 128)))]\n    #[case::max_two_byte(&[0xFF, 0x7F], Ok((vec![], 16383)))]\n    #[case::min_three_byte(&[0x80, 0x80, 0x01], Ok((vec![], 16384)))]\n    #[case::non_canonical_two(&[0x82, 0x00], Err(MltError::NonCanonicalVarInt))]\n    #[case::non_canonical_three_byte(&[0x80, 0x80, 0x00], Err(MltError::NonCanonicalVarInt))]\n    #[case::single_byte_with_trailing(&[0x01, 0x02, 0x03], Ok((vec![2, 3], 1)))]\n    #[case::underflow(&[0x80, 0x80, 0x80], Err(MltError::BufferUnderflow(4, 3)))]\n    fn test_varint_parsing(#[case] bytes: &[u8], #[case] expected: MltResult<(Vec<u8>, u32)>) {\n        let actual = parse_varint::<u32>(bytes);\n        // matching because MltError cannot implement PartialEq\n        // effectively assert_eq!(actual, expected);\n        match (actual, expected) {\n            (Ok((v1, s1)), Ok((v2, s2))) => assert_eq!((v1, s1), (v2.as_slice(), s2)),\n            (Err(actual), Err(expected)) => assert_eq!(actual.to_string(), expected.to_string()),\n            (Ok(_), Err(_)) | (Err(_), Ok(_)) => panic!(\"Unexpected result\"),\n        }\n    }\n\n    #[test]\n    fn test_parse_varint_vec() {\n        // Encode [1u32, 2, 3] as varints and parse back.\n        let mut buf = Vec::new();\n        let mut buf_tmp = vec![0u8; 10];\n        for v in [1u32, 2, 3] {\n            let written = v.encode_var(&mut buf_tmp);\n            buf.extend_from_slice(&buf_tmp[0..written]);\n        }\n        let (remaining, values) =\n            parse_varint_vec::<u32, u32>(&buf, 3, &mut dec()).expect(\"parse_varint_vec failed\");\n        assert!(remaining.is_empty());\n        assert_eq!(values, [1, 2, 3]);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/codecs/zigzag.rs",
    "content": "use num_traits::{AsPrimitive, WrappingAdd, WrappingSub};\nuse zigzag::ZigZag;\n\nuse crate::MltError::InvalidPairStreamSize;\nuse crate::{Decoder, MltResult};\n\n/// ZigZag-encode `data` into `target`.\n///\n/// `target` is treated as a scratch buffer: cleared before writing.\npub fn encode_zigzag<'a, T: ZigZag>(data: &[T], target: &'a mut Vec<T::UInt>) -> &'a [T::UInt] {\n    target.clear();\n    target.extend(data.iter().map(|&v| T::encode(v)));\n    target\n}\n\n/// Delta-then-ZigZag-encode `data` into `target` in a single pass.\n///\n/// `target` is treated as a scratch buffer: cleared before writing.\n/// Fuses the delta and zigzag steps to avoid an intermediate allocation.\npub fn encode_zigzag_delta<'a, T: Copy + ZigZag + WrappingSub<Output = T>>(\n    data: &[T],\n    target: &'a mut Vec<T::UInt>,\n) -> &'a [T::UInt] {\n    target.clear();\n    target.reserve(data.len());\n    let mut prev = T::zero();\n    for &v in data {\n        target.push(T::encode(v.wrapping_sub(&prev)));\n        prev = v;\n    }\n    target\n}\n\n/// Encode signed integer vec2 values using componentwise delta + zigzag into `target`.\n///\n/// Input: `[x0, y0, x1, y1, ...]`\n/// Output: `[zigzag(x0-0), zigzag(y0-0), zigzag(x1-x0), zigzag(y1-y0), ...]`\n///\n/// `target` is treated as a scratch buffer: cleared before writing.\n/// This is the inverse of `decode_componentwise_delta_vec2s`.\npub fn encode_componentwise_delta_vec2s<'a, T>(\n    data: &[T],\n    target: &'a mut Vec<T::UInt>,\n) -> &'a [T::UInt]\nwhere\n    T: ZigZag + WrappingSub,\n{\n    target.clear();\n    target.reserve(data.len());\n    let mut prev_x = T::zero();\n    let mut prev_y = T::zero();\n    for chunk in data.chunks_exact(2) {\n        let (x, y) = (chunk[0], chunk[1]);\n        target.push(T::encode(x.wrapping_sub(&prev_x)));\n        target.push(T::encode(y.wrapping_sub(&prev_y)));\n        (prev_x, prev_y) = (x, y);\n    }\n    target\n}\n\n/// ZigZag-decode a slice, charging `dec` for the output allocation.\npub fn decode_zigzag<T: ZigZag>(data: &[T::UInt], dec: &mut Decoder) -> MltResult<Vec<T>> {\n    dec.consume_items::<T>(data.len())?;\n    Ok(data.iter().map(|&v| T::decode(v)).collect())\n}\n\n/// Decode a vector of ZigZag-encoded unsigned deltas, charging `dec` for the output allocation.\npub fn decode_zigzag_delta<T: Copy + ZigZag + WrappingAdd + AsPrimitive<U>, U: 'static + Copy>(\n    data: &[T::UInt],\n    dec: &mut Decoder,\n) -> MltResult<Vec<U>> {\n    dec.consume_items::<U>(data.len())?;\n    Ok(data\n        .iter()\n        .scan(T::zero(), |state, &v| {\n            *state = state.wrapping_add(&T::decode(v));\n            Some((*state).as_())\n        })\n        .collect())\n}\n\n/// Decode ([`ZigZag`] + delta) for Vec2s, charging `dec` for the output allocation.\n// TODO: The encoded process is (delta + ZigZag) for each component\npub fn decode_componentwise_delta_vec2s<T: ZigZag + WrappingAdd>(\n    data: &[T::UInt],\n    dec: &mut Decoder,\n) -> MltResult<Vec<T>> {\n    if data.is_empty() || !data.len().is_multiple_of(2) {\n        return Err(InvalidPairStreamSize(data.len()));\n    }\n\n    let alloc_size = data.len();\n    let mut result = dec.alloc(alloc_size)?;\n    let mut last1 = T::zero();\n    let mut last2 = T::zero();\n\n    for i in (0..data.len()).step_by(2) {\n        last1 = last1.wrapping_add(&T::decode(data[i]));\n        last2 = last2.wrapping_add(&T::decode(data[i + 1]));\n        result.push(last1);\n        result.push(last2);\n    }\n\n    dec.adjust_alloc(&result, alloc_size)?;\n    Ok(result)\n}\n\n#[cfg(test)]\nmod tests {\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::test_helpers::dec;\n\n    proptest! {\n        #[test]\n        fn test_zigzag_roundtrip_i64(data: Vec<i64>) {\n            let mut encoded = Vec::new();\n            let decoded = decode_zigzag::<i64>(encode_zigzag(&data, &mut encoded), &mut dec()).unwrap();\n            prop_assert_eq!(data, decoded);\n        }\n\n        #[test]\n        fn test_delta_roundtrip_i32(data: Vec<i32>) {\n            if data.is_empty() { return Ok(()); }\n            let mut encoded = Vec::new();\n            encode_zigzag_delta(&data, &mut encoded);\n            let decoded: Vec<i32> = decode_zigzag_delta::<i32, i32>(&encoded, &mut dec()).unwrap();\n            prop_assert_eq!(data, decoded);\n        }\n\n        #[test]\n        fn test_componentwise_delta_vec2s(data: Vec<i32>) {\n            if data.len() <= 1 {\n                return Err(TestCaseError::reject(\"data not valid vertices\"))\n            }\n            // done this way to not have to reject less\n            let data_slice = if data.len().is_multiple_of(2) {\n                &data\n            } else {\n                &data[..data.len() - 1]\n            };\n            let mut encoded = Vec::new();\n            let data = encode_componentwise_delta_vec2s(data_slice, &mut encoded);\n            let decoded = decode_componentwise_delta_vec2s::<i32>(data, &mut dec()).unwrap();\n            prop_assert_eq!(data_slice, &decoded);\n        }\n    }\n\n    #[test]\n    fn test_encode_zigzag_empty() {\n        let mut target = Vec::<u32>::new();\n        assert!(encode_zigzag::<i32>(&[], &mut target).is_empty());\n    }\n\n    #[test]\n    fn test_encode_zigzag_delta_empty() {\n        let mut target = Vec::<u32>::new();\n        encode_zigzag_delta::<i32>(&[], &mut target);\n        assert!(target.is_empty());\n    }\n\n    #[test]\n    fn test_decode_zigzag_i32() {\n        let encoded_u32 = [0u32, 1, 2, 3, 4, 5, u32::MAX];\n        let expected_i32 = [0i32, -1, 1, -2, 2, -3, i32::MIN];\n        let decoded_i32 = decode_zigzag::<i32>(&encoded_u32, &mut dec()).unwrap();\n        assert_eq!(decoded_i32, expected_i32);\n    }\n\n    #[test]\n    fn test_decode_zigzag_i64() {\n        let encoded_u64 = [0u64, 1, 2, 3, 4, 5, u64::MAX];\n        let expected_i64 = [0i64, -1, 1, -2, 2, -3, i64::MIN];\n        let decoded_i64 = decode_zigzag::<i64>(&encoded_u64, &mut dec()).unwrap();\n        assert_eq!(decoded_i64, expected_i64);\n    }\n\n    #[test]\n    fn test_decode_zigzag_empty() {\n        assert!(decode_zigzag::<i32>(&[], &mut dec()).unwrap().is_empty());\n    }\n\n    #[test]\n    fn test_decode_zigzag_delta_empty() {\n        assert!(\n            decode_zigzag_delta::<i32, i32>(&[], &mut dec())\n                .unwrap()\n                .is_empty()\n        );\n    }\n\n    #[test]\n    fn test_decode_componentwise_delta_vec2s() {\n        let values = &[1_u32, 2, 3, 4];\n        let decoded = decode_componentwise_delta_vec2s::<i32>(values, &mut dec()).unwrap();\n        assert_eq!(&decoded, &[-1_i32, 1, -3, 3]);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/convert/geojson.rs",
    "content": "//! `GeoJSON` -like data to represent decoded MLT data with i32 coordinates\n\nuse std::collections::BTreeMap;\nuse std::str::FromStr;\n\nuse geo_types::Geometry;\nuse serde::ser::SerializeMap as _;\nuse serde::{Deserialize, Serialize};\nuse serde_json::{Number, Value};\n\nuse crate::decoder::{Layer, PropValueRef};\nuse crate::{LendingIterator, MltResult, ParsedLayer};\n\n/// `GeoJSON` [`FeatureCollection`]\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub struct FeatureCollection {\n    #[serde(rename = \"type\")]\n    pub ty: String,\n    pub features: Vec<Feature>,\n}\n\nimpl FeatureCollection {\n    /// Convert already-decoded layers to a `GeoJSON` [`FeatureCollection`], consuming them.\n    /// Make sure to call `decode_all` on Layer before calling this (won't compile otherwise)\n    pub fn from_layers<'a>(layers: impl IntoIterator<Item = ParsedLayer<'a>>) -> MltResult<Self> {\n        let mut features = Vec::new();\n        for layer in layers {\n            let Layer::Tag01(parsed) = layer else {\n                continue;\n            };\n            let layer_name = parsed.name;\n            let extent = parsed.extent;\n            let mut feat_iter = parsed.iter_features();\n            while let Some(feat) = feat_iter.next() {\n                let feat = feat?;\n                let mut properties = BTreeMap::new();\n                for p in feat.iter_properties() {\n                    properties.insert(p.name.to_string(), p.value.into());\n                }\n                properties.insert(\"_layer\".into(), Value::String(layer_name.to_string()));\n                properties.insert(\"_extent\".into(), Value::Number(extent.into()));\n                features.push(Feature {\n                    geometry: feat.geometry,\n                    id: feat.id,\n                    properties,\n                    ty: \"Feature\".into(),\n                });\n            }\n        }\n        Ok(Self {\n            features,\n            ty: \"FeatureCollection\".into(),\n        })\n    }\n\n    pub fn equals(&self, other: &Self) -> Result<bool, serde_json::Error> {\n        let self_val = normalize_tiny_floats(serde_json::to_value(self)?);\n        let other_val = normalize_tiny_floats(serde_json::to_value(other)?);\n        Ok(json_values_equal(&self_val, &other_val))\n    }\n}\n\nimpl FromStr for FeatureCollection {\n    type Err = serde_json::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        serde_json::from_str(s)\n    }\n}\n\n/// `GeoJSON` [`Feature`]\n#[derive(Debug, Clone, PartialEq, Deserialize)]\npub struct Feature {\n    #[serde(with = \"geom_serde\")]\n    pub geometry: Geometry<i32>,\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    pub id: Option<u64>,\n    pub properties: BTreeMap<String, Value>,\n    #[serde(rename = \"type\")]\n    pub ty: String,\n}\n\nstruct Geom32Wire<'a>(&'a Geometry<i32>);\nimpl Serialize for Geom32Wire<'_> {\n    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {\n        geom_serde::serialize(self.0, s)\n    }\n}\n\n/// Serialize with the preferred order of the keys\nimpl Serialize for Feature {\n    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        let len = 3 + usize::from(self.id.is_some());\n        let mut map = serializer.serialize_map(Some(len))?;\n        map.serialize_entry(\"type\", &self.ty)?;\n        if let Some(id) = self.id {\n            map.serialize_entry(\"id\", &id)?;\n        }\n        map.serialize_entry(\"properties\", &self.properties)?;\n        map.serialize_entry(\"geometry\", &Geom32Wire(&self.geometry))?;\n        map.end()\n    }\n}\n\n/// Serialize/deserialize [`Geometry<i32>`](geo_types::Geometry) in `GeoJSON` wire format:\n/// `{\"type\":\"…\",\"coordinates\":…}` with `[x, y]` integer arrays.\nmod geom_serde {\n    use geo_types::{\n        Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,\n    };\n    use serde::de::Error as _;\n    use serde::ser::{Error, SerializeMap as _};\n    use serde::{Deserialize, Deserializer, Serializer};\n    use serde_json::Value;\n\n    type Arr = [i32; 2];\n\n    fn ls_arr(ls: &LineString<i32>) -> Vec<Arr> {\n        ls.0.iter().copied().map(Into::into).collect()\n    }\n\n    fn poly_arr(poly: &Polygon<i32>) -> Vec<Vec<Arr>> {\n        std::iter::once(poly.exterior())\n            .chain(poly.interiors())\n            .map(ls_arr)\n            .collect()\n    }\n\n    fn arr_ls(v: Vec<Arr>) -> LineString<i32> {\n        LineString::from(v)\n    }\n\n    fn arr_poly(rings: Vec<Vec<Arr>>) -> Polygon<i32> {\n        let mut it = rings.into_iter();\n        let ext = it.next().map_or_else(|| LineString(vec![]), arr_ls);\n        Polygon::new(ext, it.map(arr_ls).collect())\n    }\n\n    pub fn serialize<S: Serializer>(g: &Geometry<i32>, s: S) -> Result<S::Ok, S::Error> {\n        let mut m = s.serialize_map(Some(2))?;\n        let (ty, coords): (&str, Value) = match g {\n            Geometry::Point(p) => (\"Point\", serde_json::to_value(Arr::from(*p)).unwrap()),\n            Geometry::LineString(ls) => (\"LineString\", serde_json::to_value(ls_arr(ls)).unwrap()),\n            Geometry::Polygon(poly) => (\"Polygon\", serde_json::to_value(poly_arr(poly)).unwrap()),\n            Geometry::MultiPoint(mp) => (\n                \"MultiPoint\",\n                serde_json::to_value(mp.0.iter().copied().map(Arr::from).collect::<Vec<_>>())\n                    .unwrap(),\n            ),\n            Geometry::MultiLineString(mls) => (\n                \"MultiLineString\",\n                serde_json::to_value(mls.iter().map(ls_arr).collect::<Vec<_>>()).unwrap(),\n            ),\n            Geometry::MultiPolygon(mpoly) => (\n                \"MultiPolygon\",\n                serde_json::to_value(mpoly.iter().map(poly_arr).collect::<Vec<_>>()).unwrap(),\n            ),\n            _ => return Err(Error::custom(\"unsupported geometry variant\")),\n        };\n        m.serialize_entry(\"type\", ty)?;\n        m.serialize_entry(\"coordinates\", &coords)?;\n        m.end()\n    }\n\n    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Geometry<i32>, D::Error> {\n        fn parse<T: serde::de::DeserializeOwned, E: serde::de::Error>(v: Value) -> Result<T, E> {\n            serde_json::from_value(v).map_err(E::custom)\n        }\n\n        #[derive(Deserialize)]\n        struct Wire {\n            #[serde(rename = \"type\")]\n            ty: String,\n            coordinates: Value,\n        }\n\n        let Wire { ty, coordinates: c } = Wire::deserialize(d)?;\n        Ok(match ty.as_str() {\n            \"Point\" => Geometry::Point(Point::from(parse::<Arr, _>(c)?)),\n            \"LineString\" => Geometry::LineString(arr_ls(parse(c)?)),\n            \"Polygon\" => Geometry::Polygon(arr_poly(parse(c)?)),\n            \"MultiPoint\" => {\n                let v: Vec<Arr> = parse(c)?;\n                Geometry::MultiPoint(MultiPoint(v.into_iter().map(Point::from).collect()))\n            }\n            \"MultiLineString\" => {\n                let v: Vec<Vec<Arr>> = parse(c)?;\n                Geometry::MultiLineString(MultiLineString(v.into_iter().map(arr_ls).collect()))\n            }\n            \"MultiPolygon\" => {\n                let v: Vec<Vec<Vec<Arr>>> = parse(c)?;\n                Geometry::MultiPolygon(MultiPolygon(v.into_iter().map(arr_poly).collect()))\n            }\n            _ => {\n                return Err(D::Error::unknown_variant(\n                    &ty,\n                    &[\n                        \"Point\",\n                        \"LineString\",\n                        \"Polygon\",\n                        \"MultiPoint\",\n                        \"MultiLineString\",\n                        \"MultiPolygon\",\n                    ],\n                ));\n            }\n        })\n    }\n}\n\n/// Convert f32 to `GeoJSON` value: finite as number, non-finite as string per issue #978.\n#[must_use]\npub fn f32_to_json(f: f32) -> Value {\n    if f.is_nan() {\n        Value::String(\"f32::NAN\".to_owned())\n    } else if f == f32::INFINITY {\n        Value::String(\"f32::INFINITY\".to_owned())\n    } else if f == f32::NEG_INFINITY {\n        Value::String(\"f32::NEG_INFINITY\".to_owned())\n    } else {\n        Number::from_f64(f64::from(f)).expect(\"finite f32\").into()\n    }\n}\n\n/// Convert f64 to `GeoJSON` value: finite as number, non-finite as string per issue #978.\n#[must_use]\npub fn f64_to_json(f: f64) -> Value {\n    if f.is_nan() {\n        Value::String(\"f64::NAN\".to_owned())\n    } else if f == f64::INFINITY {\n        Value::String(\"f64::INFINITY\".to_owned())\n    } else if f == f64::NEG_INFINITY {\n        Value::String(\"f64::NEG_INFINITY\".to_owned())\n    } else {\n        Number::from_f64(f).expect(\"finite f64\").into()\n    }\n}\n\nimpl From<PropValueRef<'_>> for Value {\n    fn from(v: PropValueRef<'_>) -> Self {\n        match v {\n            PropValueRef::Bool(v) => Self::Bool(v),\n            PropValueRef::I8(v) => Self::from(v),\n            PropValueRef::U8(v) => Self::from(v),\n            PropValueRef::I32(v) => Self::from(v),\n            PropValueRef::U32(v) => Self::from(v),\n            PropValueRef::I64(v) => Self::from(v),\n            PropValueRef::U64(v) => Self::from(v),\n            PropValueRef::F32(v) => f32_to_json(v),\n            PropValueRef::F64(v) => f64_to_json(v),\n            PropValueRef::Str(s) => Self::String(s.to_string()),\n        }\n    }\n}\n\n/// Replace tiny float values (e.g. `1e-40`) with `0.0` to handle codec precision issues.\nfn normalize_tiny_floats(value: Value) -> Value {\n    match value {\n        Value::Number(ref n) => {\n            let eps = f64::from(f32::EPSILON);\n            if let Some(f) = n.as_f64()\n                && f.is_finite()\n                && f.abs() < eps\n            {\n                Value::from(0.0)\n            } else {\n                value\n            }\n        }\n        Value::Array(arr) => Value::Array(arr.into_iter().map(normalize_tiny_floats).collect()),\n        Value::Object(obj) => Value::Object(\n            obj.into_iter()\n                .map(|(k, v)| (k, normalize_tiny_floats(v)))\n                .collect(),\n        ),\n        v => v,\n    }\n}\n\n/// Compare two JSON values for equality. Numbers are compared with float tolerance so that\n/// f32 round-trip (e.g. 3.14 vs 3.140000104904175) and Java minimal decimal (e.g. 3.4028235e+38)\n/// match the Rust decoder output.\nfn json_values_equal(a: &Value, b: &Value) -> bool {\n    match (a, b) {\n        (Value::Number(na), Value::Number(nb)) if na.is_f64() && nb.is_f64() => {\n            let na = na.as_f64().expect(\"f64\");\n            let nb = nb.as_f64().expect(\"f64\");\n            assert!(\n                !na.is_nan() && !nb.is_nan(),\n                \"unexpected non-finite numbers\"\n            );\n            let abs_diff = (na - nb).abs();\n            let max_abs = na.abs().max(nb.abs()).max(1.0);\n            abs_diff <= f64::from(f32::EPSILON) * max_abs * 2.0\n        }\n        (Value::Array(aa), Value::Array(ab)) => {\n            aa.len() == ab.len()\n                && aa\n                    .iter()\n                    .zip(ab.iter())\n                    .all(|(x, y)| json_values_equal(x, y))\n        }\n        (Value::Object(ao), Value::Object(bo)) => {\n            ao.len() == bo.len()\n                && ao\n                    .iter()\n                    .all(|(k, v)| bo.get(k).is_some_and(|w| json_values_equal(v, w)))\n        }\n        _ => a == b,\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/convert/mod.rs",
    "content": "pub mod geojson;\npub mod mvt;\n"
  },
  {
    "path": "rust/mlt-core/src/convert/mvt.rs",
    "content": "//! Convert MVT data to [`FeatureCollection`] or to [`TileLayer`]\n\nuse std::collections::{BTreeMap, HashMap};\n\nuse geo_types::{\n    Coord, Geometry as Geom, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon,\n    Point, Polygon,\n};\nuse mvt_reader::Reader;\nuse mvt_reader::feature::{Feature as MvtFeature, Value as MvtValue};\nuse serde_json::{Number, Value};\n\nuse crate::decoder::{PropValue, TileFeature, TileLayer};\nuse crate::geojson::{Feature, FeatureCollection};\nuse crate::{MltError, MltResult};\n\n// ── Common MVT parsing ────────────────────────────────────────────────────────\n\n/// Parsed representation of a single MVT layer: metadata plus raw features.\nstruct MvtLayer {\n    name: String,\n    extent: u32,\n    features: Vec<MvtFeature<f32>>,\n}\n\n/// Parse MVT bytes into a list of layers, each holding its raw features.\n///\n/// This is the single place where the `mvt_reader` API is called; both\n/// [`mvt_to_feature_collection`] and [`mvt_to_tile_layers`] build on top of it.\nfn read_mvt_layers(data: Vec<u8>) -> MltResult<Vec<MvtLayer>> {\n    let reader = Reader::new(data).map_err(|e| MltError::MvtParse(e.to_string()))?;\n    let metas = reader\n        .get_layer_metadata()\n        .map_err(|e| MltError::MvtParse(e.to_string()))?;\n    metas\n        .iter()\n        .map(|meta| {\n            let features = reader\n                .get_features(meta.layer_index)\n                .map_err(|e| MltError::MvtParse(e.to_string()))?;\n            Ok(MvtLayer {\n                name: meta.name.clone(),\n                extent: meta.extent,\n                features,\n            })\n        })\n        .collect()\n}\n\n/// Parse MVT binary data and convert to a [`FeatureCollection`].\npub fn mvt_to_feature_collection(data: Vec<u8>) -> MltResult<FeatureCollection> {\n    let mut features = Vec::new();\n\n    for layer in read_mvt_layers(data)? {\n        for feat in layer.features {\n            let geometry = convert_geometry(&feat.geometry)?;\n            let mut properties = feat\n                .properties\n                .map(|p| {\n                    p.into_iter()\n                        .map(|(k, v)| (k, convert_value(&v)))\n                        .collect::<BTreeMap<_, _>>()\n                })\n                .unwrap_or_default();\n            properties.insert(\"_layer\".into(), Value::String(layer.name.clone()));\n            properties.insert(\"_extent\".into(), Value::Number(layer.extent.into()));\n            features.push(Feature {\n                geometry,\n                id: feat.id,\n                properties,\n                ty: \"Feature\".into(),\n            });\n        }\n    }\n\n    Ok(FeatureCollection {\n        features,\n        ty: \"FeatureCollection\".into(),\n    })\n}\n\n/// Parse MVT binary data and convert each layer to a row-oriented [`TileLayer`].\n///\n/// Each MVT layer becomes one [`TileLayer`].  Property column types are inferred\n/// from all features in the layer: the first non-null value seen for each column\n/// determines its type, with `I64`+`U64` widened to `I64` and `F32`+`F64` widened\n/// to `F64`; all other type conflicts fall back to `Str`.\npub fn mvt_to_tile_layers(data: Vec<u8>) -> MltResult<Vec<TileLayer>> {\n    read_mvt_layers(data)?\n        .into_iter()\n        .map(mvt_layer_to_tile)\n        .collect()\n}\n\nfn mvt_layer_to_tile(layer: MvtLayer) -> MltResult<TileLayer> {\n    // First pass: collect property names (insertion-ordered) and infer column types.\n    let mut col_names: Vec<String> = Vec::new();\n    let mut col_index: HashMap<String, usize> = HashMap::new();\n    let mut col_types: Vec<InferredType> = Vec::new();\n\n    for feat in &layer.features {\n        let Some(props) = &feat.properties else {\n            continue;\n        };\n        for (key, val) in props {\n            let idx = *col_index.entry(key.clone()).or_insert_with(|| {\n                let i = col_names.len();\n                col_names.push(key.clone());\n                col_types.push(InferredType::Unknown);\n                i\n            });\n            col_types[idx] = col_types[idx].merge(InferredType::from_mvt(val));\n        }\n    }\n\n    // Columns that were only ever null fall back to Str.\n    for t in &mut col_types {\n        if *t == InferredType::Unknown {\n            *t = InferredType::Str;\n        }\n    }\n\n    // Second pass: build TileFeature objects.\n    let mut tile_features = Vec::with_capacity(layer.features.len());\n    for feat in layer.features {\n        let geometry = convert_geometry(&feat.geometry)?;\n        // Start every slot with a typed null; fill in present values below.\n        let mut properties: Vec<PropValue> = col_types.iter().map(|t| t.typed_null()).collect();\n        if let Some(props) = feat.properties {\n            for (key, val) in props {\n                if let Some(&idx) = col_index.get(&key)\n                    && !matches!(val, MvtValue::Null)\n                {\n                    properties[idx] = col_types[idx].convert(val);\n                }\n            }\n        }\n        tile_features.push(TileFeature {\n            id: feat.id,\n            geometry,\n            properties,\n        });\n    }\n\n    Ok(TileLayer {\n        name: layer.name,\n        extent: layer.extent,\n        property_names: col_names,\n        features: tile_features,\n    })\n}\n\nfn coord(c: impl AsRef<Coord<f32>>) -> Coord<i32> {\n    let c = c.as_ref();\n    #[expect(clippy::cast_possible_truncation)]\n    Coord {\n        x: c.x.round() as i32,\n        y: c.y.round() as i32,\n    }\n}\n\nfn convert_geometry(geom: &Geom<f32>) -> MltResult<Geometry<i32>> {\n    Ok(match geom {\n        Geom::Point(v) => Geometry::<i32>::Point(Point(coord(v))),\n        Geom::MultiPoint(v) => {\n            Geometry::<i32>::MultiPoint(MultiPoint(v.iter().map(|p| Point(coord(p))).collect()))\n        }\n        Geom::LineString(v) => {\n            Geometry::<i32>::LineString(LineString(v.coords().map(coord).collect()))\n        }\n        Geom::MultiLineString(v) => Geometry::<i32>::MultiLineString(MultiLineString(\n            v.iter()\n                .map(|ls| LineString(ls.coords().map(coord).collect()))\n                .collect(),\n        )),\n        Geom::Polygon(v) => Geometry::<i32>::Polygon(convert_polygon(v)),\n        Geom::MultiPolygon(v) => {\n            Geometry::<i32>::MultiPolygon(MultiPolygon(v.iter().map(convert_polygon).collect()))\n        }\n        Geom::GeometryCollection(v) => {\n            return if v.len() == 1 {\n                convert_geometry(&v[0])\n            } else {\n                Err(MltError::BadMvtGeometry(\n                    \"multiple geometries in a collection are not supported\",\n                ))\n            };\n        }\n        Geom::Line(_) => Err(MltError::BadMvtGeometry(\"Unsupported Line geo type\"))?,\n        Geom::Rect(_) => Err(MltError::BadMvtGeometry(\"Unsupported Rect geo type\"))?,\n        Geom::Triangle(_) => Err(MltError::BadMvtGeometry(\"Unsupported Triangle geo type\"))?,\n    })\n}\n\nfn convert_polygon(poly: &Polygon<f32>) -> Polygon<i32> {\n    let exterior = LineString(poly.exterior().coords().map(coord).collect());\n    let interiors = poly\n        .interiors()\n        .iter()\n        .map(|r| LineString(r.coords().map(coord).collect()))\n        .collect();\n    Polygon::new(exterior, interiors)\n}\n\nfn convert_value(val: &MvtValue) -> Value {\n    match val {\n        MvtValue::String(s) => Value::String(s.clone()),\n        MvtValue::Float(f) => Number::from_f64(f64::from(*f)).map_or(Value::Null, Value::Number),\n        MvtValue::Double(f) => Number::from_f64(*f).map_or(Value::Null, Value::Number),\n        MvtValue::Int(i) | MvtValue::SInt(i) => Value::Number((*i).into()),\n        MvtValue::UInt(u) => Value::Number((*u).into()),\n        MvtValue::Bool(b) => Value::Bool(*b),\n        MvtValue::Null => Value::Null,\n    }\n}\n\n/// Column type inferred from MVT property values across all features in a layer.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum InferredType {\n    Unknown,\n    Bool,\n    I64,\n    U64,\n    F32,\n    F64,\n    Str,\n}\n\nimpl InferredType {\n    fn from_mvt(val: &MvtValue) -> Self {\n        match val {\n            MvtValue::Bool(_) => Self::Bool,\n            MvtValue::Int(_) | MvtValue::SInt(_) => Self::I64,\n            MvtValue::UInt(_) => Self::U64,\n            MvtValue::Float(_) => Self::F32,\n            MvtValue::Double(_) => Self::F64,\n            MvtValue::String(_) => Self::Str,\n            MvtValue::Null => Self::Unknown,\n        }\n    }\n\n    /// Merge with another type, widening when necessary.\n    fn merge(self, other: Self) -> Self {\n        if self == Self::Unknown {\n            return other;\n        }\n        if other == Self::Unknown || self == other {\n            return self;\n        }\n        if matches!(\n            (self, other),\n            (Self::I64, Self::U64) | (Self::U64, Self::I64)\n        ) {\n            return Self::I64;\n        }\n        if matches!(\n            (self, other),\n            (Self::F32, Self::F64) | (Self::F64, Self::F32)\n        ) {\n            return Self::F64;\n        }\n        Self::Str\n    }\n\n    fn typed_null(self) -> PropValue {\n        match self {\n            Self::Unknown | Self::Str => PropValue::Str(None),\n            Self::Bool => PropValue::Bool(None),\n            Self::I64 => PropValue::I64(None),\n            Self::U64 => PropValue::U64(None),\n            Self::F32 => PropValue::F32(None),\n            Self::F64 => PropValue::F64(None),\n        }\n    }\n\n    /// Convert an owned [`MvtValue`] into a [`PropValue`] matching this column type.\n    fn convert(self, val: MvtValue) -> PropValue {\n        match (self, val) {\n            (_, MvtValue::Null) => self.typed_null(),\n            (Self::Bool, MvtValue::Bool(b)) => PropValue::Bool(Some(b)),\n            (Self::I64, MvtValue::Int(i) | MvtValue::SInt(i)) => PropValue::I64(Some(i)),\n            (Self::I64, MvtValue::UInt(u)) if i64::try_from(u).is_ok() => {\n                // Value must be within 0..i64::MAX\n                #[expect(clippy::cast_possible_wrap, reason = \"checked above\")]\n                PropValue::I64(Some(u as i64))\n            }\n            (Self::U64, MvtValue::UInt(u)) => PropValue::U64(Some(u)),\n            (Self::F32, MvtValue::Float(f)) => PropValue::F32(Some(f)),\n            (Self::F64, MvtValue::Double(f)) => PropValue::F64(Some(f)),\n            (Self::F64, MvtValue::Float(f)) => PropValue::F64(Some(f64::from(f))),\n            (_, MvtValue::String(s)) => PropValue::Str(Some(s)),\n            // Type conflict at runtime: fall back to a debug string.\n            (_, v) => PropValue::Str(Some(format!(\"{v:?}\"))),\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/analyze.rs",
    "content": "use crate::decoder::{\n    Geometry, GeometryType, GeometryValues, Id, Layer01, Property, RawFsstData, RawGeometry, RawId,\n    RawIdValue, RawPlainData, RawPresence, RawProperty, RawScalar, RawSharedDict,\n    RawSharedDictEncoding, RawSharedDictItem, RawStrings, RawStringsEncoding, StreamMeta,\n};\nuse crate::{Analyze, DecodeState, StatType};\n\nimpl<'a, S: DecodeState> Analyze for Layer01<'a, S>\nwhere\n    Option<Id<'a, S>>: Analyze,\n    Geometry<'a, S>: Analyze,\n    Vec<Property<'a, S>>: Analyze,\n{\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        match stat {\n            StatType::DecodedMetaSize => self.name.len() + size_of::<u32>(),\n            StatType::DecodedDataSize => {\n                self.id.collect_statistic(stat)\n                    + self.geometry.collect_statistic(stat)\n                    + self.properties.collect_statistic(stat)\n            }\n            StatType::FeatureCount => self.geometry.collect_statistic(stat),\n        }\n    }\n\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.id.for_each_stream(cb);\n        self.geometry.for_each_stream(cb);\n        self.properties.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawGeometry<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.meta.for_each_stream(cb);\n        self.items.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for GeometryValues {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        match stat {\n            StatType::DecodedDataSize => {\n                self.vector_types.collect_statistic(stat)\n                    + self.geometry_offsets.collect_statistic(stat)\n                    + self.part_offsets.collect_statistic(stat)\n                    + self.ring_offsets.collect_statistic(stat)\n                    + self.index_buffer.collect_statistic(stat)\n                    + self.triangles.collect_statistic(stat)\n                    + self.vertices.collect_statistic(stat)\n            }\n            StatType::DecodedMetaSize => 0,\n            StatType::FeatureCount => self.vector_types.len(),\n        }\n    }\n}\n\nimpl Analyze for GeometryType {\n    fn collect_statistic(&self, _stat: StatType) -> usize {\n        size_of::<Self>()\n    }\n}\n\nimpl Analyze for RawId<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.presence.for_each_stream(cb);\n        self.value.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawIdValue<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        match self {\n            Self::Id32(v) | Self::Id64(v) => v.for_each_stream(cb),\n        }\n    }\n}\n\nimpl Analyze for RawPresence<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.0.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawPlainData<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.lengths.for_each_stream(cb);\n        self.data.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawFsstData<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.symbol_lengths.for_each_stream(cb);\n        self.symbol_table.for_each_stream(cb);\n        self.lengths.for_each_stream(cb);\n        self.corpus.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawStringsEncoding<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        match self {\n            Self::Plain(plain_data) => plain_data.for_each_stream(cb),\n            Self::Dictionary {\n                plain_data,\n                offsets,\n            } => {\n                plain_data.for_each_stream(cb);\n                offsets.for_each_stream(cb);\n            }\n            Self::FsstPlain(fsst_data) => fsst_data.for_each_stream(cb),\n            Self::FsstDictionary { fsst_data, offsets } => {\n                fsst_data.for_each_stream(cb);\n                offsets.for_each_stream(cb);\n            }\n        }\n    }\n}\n\nimpl Analyze for RawScalar<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.presence.for_each_stream(cb);\n        self.data.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawStrings<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.presence.for_each_stream(cb);\n        self.encoding.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawSharedDictItem<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.presence.for_each_stream(cb);\n        self.data.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawSharedDictEncoding<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        match self {\n            Self::Plain(plain_data) => plain_data.for_each_stream(cb),\n            Self::FsstPlain(fsst_data) => fsst_data.for_each_stream(cb),\n        }\n    }\n}\n\nimpl Analyze for RawSharedDict<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.encoding.for_each_stream(cb);\n        self.children.for_each_stream(cb);\n    }\n}\n\nimpl Analyze for RawProperty<'_> {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        match self {\n            Self::Bool(s)\n            | Self::I8(s)\n            | Self::U8(s)\n            | Self::I32(s)\n            | Self::U32(s)\n            | Self::I64(s)\n            | Self::U64(s)\n            | Self::F32(s)\n            | Self::F64(s) => s.for_each_stream(cb),\n            Self::Str(s) => s.for_each_stream(cb),\n            Self::SharedDict(s) => s.for_each_stream(cb),\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/column.rs",
    "content": "use std::io;\nuse std::io::Write;\n\nuse crate::MltError::ParsingColumnType;\nuse crate::decoder::{Column, ColumnType};\nuse crate::utils::{BinarySerializer as _, parse_string, parse_u8};\nuse crate::{MltRefResult, Parser};\n\nimpl Column<'_> {\n    /// Parse a single column definition\n    pub(crate) fn from_bytes<'a>(\n        input: &'a [u8],\n        _parser: &mut Parser,\n    ) -> MltRefResult<'a, Column<'a>> {\n        let (mut input, typ) = ColumnType::from_bytes(input)?;\n        let name = if typ.has_name() {\n            let pair = parse_string(input)?;\n            input = pair.0;\n            Some(pair.1)\n        } else {\n            None\n        };\n\n        Ok((\n            input,\n            Column {\n                typ,\n                name,\n                children: Vec::new(),\n            },\n        ))\n    }\n}\n\nimpl ColumnType {\n    /// Parse a column type from u8\n    pub(crate) fn from_bytes(input: &[u8]) -> MltRefResult<'_, Self> {\n        let (input, value) = parse_u8(input)?;\n        let value = Self::try_from(value).or(Err(ParsingColumnType(value)))?;\n        Ok((input, value))\n    }\n\n    pub(crate) fn write_to<W: Write>(self, writer: &mut W) -> io::Result<()> {\n        writer.write_u8(self as u8)?;\n        Ok(())\n    }\n\n    /// Returns true if the column definition includes a name field in the serialized format.\n    /// Note: ID and Geometry columns use implicit naming and do not include a name field.\n    #[must_use]\n    pub(crate) fn has_name(self) -> bool {\n        !matches!(\n            self,\n            Self::Id | Self::OptId | Self::LongId | Self::OptLongId | Self::Geometry\n        )\n    }\n\n    /// Check if the column type has a presence stream\n    #[must_use]\n    pub(crate) fn is_optional(self) -> bool {\n        (self as u8) & 1 != 0\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/fuzzing.rs",
    "content": "use arbitrary::{Arbitrary, Result, Unstructured};\nuse geo_types::{Coord, Geometry, Point};\n\n#[cfg(fuzzing)]\nuse crate::decoder::ColumnType;\nuse crate::decoder::GeometryValues;\n#[allow(\n    unused_imports,\n    clippy::wildcard_imports,\n    reason = \"not worth for fuzzing\"\n)]\nuse crate::*;\n\n#[cfg(fuzzing)]\n/// To make sure we serialize out in the same order as the original file, we need to store the order in which we parsed the columns\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub enum LayerOrdering {\n    Id,\n    Geometry,\n    Property,\n}\n\n#[cfg(fuzzing)]\nimpl From<ColumnType> for LayerOrdering {\n    fn from(typ: ColumnType) -> Self {\n        use ColumnType::*;\n        match typ {\n            OptId | Id | LongId | OptLongId => Self::Id,\n            Bool | OptBool | I8 | OptI8 | U8 | OptU8 | I32 | OptI32 | U32 | OptU32 | I64\n            | OptI64 | U64 | OptU64 | F32 | OptF32 | F64 | OptF64 | Str | OptStr | SharedDict => {\n                Self::Property\n            }\n            Geometry => Self::Geometry,\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, PartialOrd, Arbitrary)]\nenum ArbitraryGeometry {\n    Point((i32, i32)),\n    // FIXME: Add LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, once supported upstream\n}\n\nimpl From<ArbitraryGeometry> for Geometry<i32> {\n    fn from(value: ArbitraryGeometry) -> Self {\n        match value {\n            ArbitraryGeometry::Point((x, y)) => Self::Point(Point(Coord::<i32> { x, y })), // FIXME: once fully working, add the rest\n        }\n    }\n}\n\nimpl Arbitrary<'_> for GeometryValues {\n    fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {\n        // Bound geometry count to prevent OOM from unbounded iteration\n        let count = u.int_in_range(1..=32u16)? as usize;\n        let mut decoded = Self::default();\n        for _ in 0..count {\n            let geo: ArbitraryGeometry = u.arbitrary()?;\n            decoded.push_geom(&Geometry::<i32>::from(geo));\n        }\n        Ok(decoded)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/geometry/decode.rs",
    "content": "use crate::codecs::varint::parse_varint;\nuse crate::decoder::{\n    DictionaryType, GeometryType, GeometryValues, IntEncoding, LengthType, OffsetType, RawGeometry,\n    RawStream, StreamMeta, StreamType,\n};\nuse crate::errors::AsMltError as _;\nuse crate::utils::{AsUsize as _, SetOptionOnce as _};\nuse crate::{Decode, Decoder, MltError, MltResult, Parser};\n\n/// Advance `offset` by `count` and extend `buffer` with the consecutive values\n/// `old_offset + 1, old_offset + 2, …, new_offset`.\nfn push_consecutive_offsets(\n    buffer: &mut Vec<u32>,\n    offset: &mut u32,\n    count: usize,\n) -> MltResult<()> {\n    if count > 0 {\n        let count = u32::try_from(count).or_overflow()?;\n        *offset = offset.checked_add(count).or_overflow()?;\n        // Safety: offset+1 cannot overflow because offset+count didn't and count >= 1.\n        buffer.extend((*offset - count + 1)..=*offset);\n    }\n    Ok(())\n}\n\npub fn decode_geometry_types(\n    meta: RawStream<'_>,\n    dec: &mut Decoder,\n) -> MltResult<Vec<GeometryType>> {\n    // TODO: simplify this, e.g. use u8 or even GeometryType directly rather than going via Vec<u32>\n    let vector_types: Vec<u32> = meta.decode_u32s(dec)?;\n    let vector_types: Vec<GeometryType> = vector_types\n        .into_iter()\n        .map::<MltResult<GeometryType>, _>(|v| Ok(u8::try_from(v)?.try_into()?))\n        .collect::<Result<_, _>>()?;\n    Ok(vector_types)\n}\n\n/// Handle the parsing of the different topology length buffers separate not generic to reduce the\n/// branching and improve the performance\npub fn decode_root_length_stream(\n    geometry_types: &[GeometryType],\n    root_length_stream: &[u32],\n    buffer_id: GeometryType,\n    dec: &mut Decoder,\n) -> MltResult<Vec<u32>> {\n    let alloc_size = geometry_types.len().checked_add(1).or_overflow()?;\n    let mut root_buffer_offsets = dec.alloc(alloc_size)?;\n\n    root_buffer_offsets.push(0);\n    let mut offset = 0_u32;\n    let mut root_length_counter = 0_usize;\n    for &geom_type in geometry_types {\n        let increment = if geom_type > buffer_id {\n            let val = *root_length_stream\n                .get(root_length_counter)\n                .ok_or(MltError::GeometryIndexOutOfBounds(root_length_counter))?;\n            // Safety: counter bounded by `geometry_types.len()`, which fits in usize.\n            root_length_counter += 1;\n            val\n        } else {\n            1\n        };\n        offset = offset.checked_add(increment).or_overflow()?;\n        root_buffer_offsets.push(offset);\n    }\n\n    dec.adjust_alloc(&root_buffer_offsets, alloc_size)?;\n    Ok(root_buffer_offsets)\n}\n\n/// Case where no ring buffer exists so no `MultiPolygon` or `Polygon` geometry is part of the buffer\npub fn decode_level1_without_ring_buffer_length_stream(\n    geometry_types: &[GeometryType],\n    root_offset_buffer: &[u32],\n    level1_length_buffer: &[u32],\n    dec: &mut Decoder,\n) -> MltResult<Vec<u32>> {\n    // Safety: root_offset_buffer is produced by decode_root_length_stream, which always\n    // pushes an initial 0, so it is never empty.\n    let alloc_size = root_offset_buffer[root_offset_buffer.len() - 1]\n        .as_usize()\n        .checked_add(1)\n        .or_overflow()?;\n    let mut level1_buffer_offsets = dec.alloc(alloc_size)?;\n    level1_buffer_offsets.push(0);\n    let mut offset = 0_u32;\n    let mut level1_length_counter = 0_usize;\n\n    for (&geometry_type, w) in geometry_types.iter().zip(root_offset_buffer.windows(2)) {\n        let num_geometries = w[1].checked_sub(w[0]).or_overflow()?.as_usize();\n\n        if geometry_type.is_linestring() {\n            // For MultiLineString and LineString a value in the level1LengthBuffer exists\n            for _j in 0..num_geometries {\n                let val = *level1_length_buffer\n                    .get(level1_length_counter)\n                    .ok_or(MltError::GeometryIndexOutOfBounds(level1_length_counter))?;\n                offset = offset.checked_add(val).or_overflow()?;\n                level1_buffer_offsets.push(offset);\n                // Safety: counter bounded by slice lengths, which fit in usize.\n                level1_length_counter += 1;\n            }\n        } else {\n            // For MultiPoint and Point no value in level1LengthBuffer exists\n            push_consecutive_offsets(&mut level1_buffer_offsets, &mut offset, num_geometries)?;\n        }\n    }\n\n    dec.adjust_alloc(&level1_buffer_offsets, alloc_size)?;\n    Ok(level1_buffer_offsets)\n}\n\npub fn decode_level1_length_stream(\n    geometry_types: &[GeometryType],\n    root_offset_buffer: &[u32],\n    level1_length_buffer: &[u32],\n    is_line_string_present: bool,\n    dec: &mut Decoder,\n) -> MltResult<Vec<u32>> {\n    // Safety: root_offset_buffer is produced by decode_root_length_stream, which always\n    // pushes an initial 0, so it is never empty.\n    let alloc_size = root_offset_buffer[root_offset_buffer.len() - 1]\n        .as_usize()\n        .checked_add(1)\n        .or_overflow()?;\n    let mut level1_buffer_offsets = dec.alloc(alloc_size)?;\n    level1_buffer_offsets.push(0);\n    let mut offset = 0_u32;\n    let mut level1_length_buffer_counter = 0_usize;\n\n    for (&geometry_type, w) in geometry_types.iter().zip(root_offset_buffer.windows(2)) {\n        let num_geometries = w[1].checked_sub(w[0]).or_overflow()?.as_usize();\n\n        if geometry_type.is_polygon() || (is_line_string_present && geometry_type.is_linestring()) {\n            // For MultiPolygon, Polygon and in some cases for MultiLineString and LineString\n            // a value in the level1LengthBuffer exists\n            for _j in 0..num_geometries {\n                let val = *level1_length_buffer\n                    .get(level1_length_buffer_counter)\n                    .ok_or(MltError::GeometryIndexOutOfBounds(\n                        level1_length_buffer_counter,\n                    ))?;\n                offset = offset.checked_add(val).or_overflow()?;\n                level1_buffer_offsets.push(offset);\n                // Safety: counter bounded by slice lengths, which fit in usize.\n                level1_length_buffer_counter += 1;\n            }\n        } else {\n            // For MultiPoint and Point and in some cases for MultiLineString and LineString\n            // no value in the level1LengthBuffer exists\n            push_consecutive_offsets(&mut level1_buffer_offsets, &mut offset, num_geometries)?;\n        }\n    }\n\n    dec.adjust_alloc(&level1_buffer_offsets, alloc_size)?;\n    Ok(level1_buffer_offsets)\n}\n\npub fn decode_level2_length_stream(\n    geometry_types: &[GeometryType],\n    root_offset_buffer: &[u32],\n    level1_offset_buffer: &[u32],\n    level2_length_buffer: &[u32],\n    dec: &mut Decoder,\n) -> MltResult<Vec<u32>> {\n    // Safety: level1_offset_buffer is produced by decode_level1_*_length_stream, which\n    // always pushes an initial 0, so it is never empty.\n    let last = level1_offset_buffer[level1_offset_buffer.len() - 1];\n    let alloc_size = last.as_usize().checked_add(1).or_overflow()?;\n    let mut level2_buffer_offsets = dec.alloc(alloc_size)?;\n    level2_buffer_offsets.push(0);\n    let mut previous_offset = 0_u32;\n    let mut level1_tail = level1_offset_buffer;\n    let mut level2_pos = 0_usize;\n\n    for (&geometry_type, w) in geometry_types.iter().zip(root_offset_buffer.windows(2)) {\n        let num_geometries = w[1].checked_sub(w[0]).or_overflow()?.as_usize();\n\n        if geometry_type != GeometryType::Point && geometry_type != GeometryType::MultiPoint {\n            // For MultiPolygon, MultiLineString, Polygon and LineString a value in level2LengthBuffer\n            // exists\n            for _j in 0..num_geometries {\n                let [base, next, ..] = *level1_tail else {\n                    return Err(MltError::IntegerOverflow);\n                };\n                let num_parts = next.checked_sub(base).or_overflow()?.as_usize();\n                level1_tail = &level1_tail[1..];\n                for _k in 0..num_parts {\n                    let val = *level2_length_buffer\n                        .get(level2_pos)\n                        .ok_or(MltError::GeometryIndexOutOfBounds(level2_pos))?;\n                    previous_offset = previous_offset.checked_add(val).or_overflow()?;\n                    // Safety: counter bounded by slice lengths, which fit in usize.\n                    level2_pos += 1;\n                    level2_buffer_offsets.push(previous_offset);\n                }\n            }\n        } else {\n            // For MultiPoint and Point no value in level2LengthBuffer exists\n            push_consecutive_offsets(\n                &mut level2_buffer_offsets,\n                &mut previous_offset,\n                num_geometries,\n            )?;\n            if num_geometries > level1_tail.len() {\n                return Err(MltError::IntegerOverflow);\n            }\n            level1_tail = &level1_tail[num_geometries..];\n        }\n    }\n\n    dec.adjust_alloc(&level2_buffer_offsets, alloc_size)?;\n    Ok(level2_buffer_offsets)\n}\n\nimpl<'a> RawGeometry<'a> {\n    /// Parse encoded geometry from bytes (expects varint stream count + streams).\n    /// Reserves decoded memory against the parser's budget.\n    pub fn from_bytes(input: &'a [u8], parser: &mut Parser) -> crate::MltRefResult<'a, Self> {\n        let (input, stream_count) = parse_varint::<u32>(input)?;\n        let stream_count = stream_count.as_usize();\n        if stream_count == 0 {\n            return Ok((\n                input,\n                Self {\n                    meta: RawStream::new(\n                        StreamMeta::new(\n                            StreamType::Data(DictionaryType::None),\n                            IntEncoding::none(),\n                            0,\n                        ),\n                        &[],\n                    ),\n                    items: Vec::new(),\n                },\n            ));\n        }\n\n        let (input, meta) = RawStream::from_bytes(input, parser)?;\n        // Safety: stream_count is validated != 0\n        let (input, items) = RawStream::parse_multiple(input, stream_count - 1, parser)?;\n\n        Ok((input, Self { meta, items }))\n    }\n}\n\nimpl Decode<GeometryValues> for RawGeometry<'_> {\n    /// Decode into [`GeometryValues`], charging `dec` before each `Vec<T>`\n    /// allocation.  All streams carry `num_values` in their metadata so every\n    /// charge is pre-hoc.\n    fn decode(self, dec: &mut Decoder) -> MltResult<GeometryValues> {\n        let RawGeometry { meta, items } = self;\n        let vector_types = decode_geometry_types(meta, dec)?;\n        let mut geometry_offsets: Option<Vec<u32>> = None;\n        let mut part_offsets: Option<Vec<u32>> = None;\n        let mut ring_offsets: Option<Vec<u32>> = None;\n        let mut vertex_offsets: Option<Vec<u32>> = None;\n        let mut index_buffer: Option<Vec<u32>> = None;\n        let mut triangles: Option<Vec<u32>> = None;\n        let mut vertices: Option<Vec<i32>> = None;\n\n        for stream in items {\n            match stream.meta.stream_type {\n                StreamType::Present => {}\n                StreamType::Data(v) => match v {\n                    DictionaryType::Vertex | DictionaryType::Morton => {\n                        vertices.set_once(stream.decode_i32s(dec)?)?;\n                    }\n                    _ => Err(MltError::UnexpectedStreamType(stream.meta.stream_type))?,\n                },\n                StreamType::Offset(v) => {\n                    let target = match v {\n                        OffsetType::Vertex => &mut vertex_offsets,\n                        OffsetType::Index => &mut index_buffer,\n                        _ => Err(MltError::UnexpectedStreamType(stream.meta.stream_type))?,\n                    };\n                    target.set_once(stream.decode_u32s(dec)?)?;\n                }\n                StreamType::Length(v) => {\n                    let target = match v {\n                        LengthType::Geometries => &mut geometry_offsets,\n                        LengthType::Parts => &mut part_offsets,\n                        LengthType::Rings => &mut ring_offsets,\n                        LengthType::Triangles => &mut triangles,\n                        _ => Err(MltError::UnexpectedStreamType(stream.meta.stream_type))?,\n                    };\n                    target.set_once(stream.decode_u32s(dec)?)?;\n                }\n            }\n        }\n\n        if index_buffer.is_some() && part_offsets.is_none() {\n            // Case when the indices of a Polygon outline are not encoded in the data so no\n            // topology data are present in the tile\n            //\n            // return FlatGpuVector::new(vector_types, triangles, index_buffer, vertices);\n            return Err(MltError::NotImplemented(\n                \"index_buffer.is_some() && part_offsets.is_none() case\",\n            ));\n        }\n\n        // Use decode_root_length_stream if geometry_offsets is present\n        if let Some(offsets) = geometry_offsets.take() {\n            geometry_offsets = Some(decode_root_length_stream(\n                &vector_types,\n                &offsets,\n                GeometryType::Polygon,\n                dec,\n            )?);\n            if let Some(part_offsets_copy) = part_offsets.take() {\n                if let Some(ring_offsets_copy) = ring_offsets.take() {\n                    part_offsets = Some(decode_level1_length_stream(\n                        &vector_types,\n                        geometry_offsets.as_ref().unwrap(),\n                        &part_offsets_copy,\n                        false, // isLineStringPresent\n                        dec,\n                    )?);\n                    ring_offsets = Some(decode_level2_length_stream(\n                        &vector_types,\n                        geometry_offsets.as_ref().unwrap(),\n                        part_offsets.as_ref().unwrap(),\n                        &ring_offsets_copy,\n                        dec,\n                    )?);\n                } else {\n                    part_offsets = Some(decode_level1_without_ring_buffer_length_stream(\n                        &vector_types,\n                        geometry_offsets.as_ref().unwrap(),\n                        &part_offsets_copy,\n                        dec,\n                    )?);\n                }\n            }\n        } else if let Some(offsets) = part_offsets.take() {\n            if let Some(ring_offsets_copy) = ring_offsets.take() {\n                let is_line_string_present = vector_types.iter().any(|t| t.is_linestring());\n                part_offsets = Some(decode_root_length_stream(\n                    &vector_types,\n                    &offsets,\n                    GeometryType::LineString,\n                    dec,\n                )?);\n                ring_offsets = Some(decode_level1_length_stream(\n                    &vector_types,\n                    part_offsets.as_ref().unwrap(),\n                    &ring_offsets_copy,\n                    is_line_string_present,\n                    dec,\n                )?);\n            } else {\n                part_offsets = Some(decode_root_length_stream(\n                    &vector_types,\n                    &offsets,\n                    GeometryType::Point,\n                    dec,\n                )?);\n            }\n        }\n\n        // Case when the indices of a Polygon outline are encoded in the tile\n        // This is handled by including index_buffer in the GeometryValues\n\n        // Expand vertex dictionary:\n        // If a vertex offset stream was present,\n        // - `vertices` holds only the unique dictionary entries and\n        // - `vertex_offsets` holds per-vertex indices into it.\n        //\n        // Expand them into a single flat (x, y) sequence so that `GeometryValues` always\n        // represents fully decoded data, regardless of the encoding that was used.\n        if let Some(offsets) = vertex_offsets.take()\n            && let Some(dict) = vertices.as_deref()\n        {\n            dec.consume_items::<[i32; 2]>(offsets.len())?;\n            // SAFETY:\n            // Check before multiplying: i < dict_vertex_count guarantees\n            // i * 2 + 1 < dict.len() with no risk of overflow, because\n            // Rust limits Vec::len() to isize::MAX, so\n            // dict_vertex_count <= isize::MAX / 2, meaning\n            // i * 2 + 1 <= isize::MAX < usize::MAX.\n            let dict_vertex_count = dict.len() / 2;\n            vertices = Some(offsets.iter().try_fold(\n                Vec::with_capacity(offsets.len() * 2),\n                |mut acc, &idx| -> MltResult<_> {\n                    let i = idx.as_usize();\n                    if i >= dict_vertex_count {\n                        return Err(MltError::DictIndexOutOfBounds(idx, dict_vertex_count));\n                    }\n                    acc.push(dict[i * 2]);\n                    acc.push(dict[i * 2 + 1]);\n                    Ok(acc)\n                },\n            )?);\n        }\n\n        Ok(GeometryValues {\n            vector_types,\n            geometry_offsets,\n            part_offsets,\n            ring_offsets,\n            index_buffer,\n            triangles,\n            vertices,\n        })\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/geometry/geotype.rs",
    "content": "use std::ops::Range;\n\nuse geo_types::{\n    Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,\n};\n\nuse crate::MltError::{\n    GeometryIndexOutOfBounds, GeometryOutOfBounds, GeometryVertexOutOfBounds, NoGeometryOffsets,\n    NoPartOffsets, NoRingOffsets,\n};\nuse crate::MltResult;\nuse crate::decoder::{GeometryType, GeometryValues};\nuse crate::utils::AsUsize as _;\n\nimpl GeometryType {\n    #[must_use]\n    pub fn is_polygon(self) -> bool {\n        matches!(self, Self::Polygon | Self::MultiPolygon)\n    }\n    #[must_use]\n    pub fn is_linestring(self) -> bool {\n        matches!(self, Self::LineString | Self::MultiLineString)\n    }\n    #[must_use]\n    pub fn is_multi(self) -> bool {\n        matches!(\n            self,\n            Self::MultiPoint | Self::MultiLineString | Self::MultiPolygon\n        )\n    }\n}\n\nimpl GeometryValues {\n    /// Geometry types for each feature, in insertion order.\n    #[must_use]\n    pub fn vector_types(&self) -> &[GeometryType] {\n        &self.vector_types\n    }\n\n    /// Cumulative offsets into `part_offsets` for multi-geometry types.\n    /// `None` when no multi-geometry features are present.\n    #[must_use]\n    pub fn geometry_offsets(&self) -> Option<&[u32]> {\n        self.geometry_offsets.as_deref()\n    }\n\n    /// Cumulative offsets into `ring_offsets` (or directly into `vertices`\n    /// for `LineString` layers without rings).\n    /// `None` for pure `Point` layers.\n    #[must_use]\n    pub fn part_offsets(&self) -> Option<&[u32]> {\n        self.part_offsets.as_deref()\n    }\n\n    /// Cumulative offsets into the vertex buffer (counting whole vertices).\n    /// `None` when no ring-level indirection is needed.\n    #[must_use]\n    pub fn ring_offsets(&self) -> Option<&[u32]> {\n        self.ring_offsets.as_deref()\n    }\n\n    /// Triangle index buffer produced by Earcut tessellation.\n    /// `None` unless the `GeometryValues` was created with [`Self::new_tessellated`].\n    #[must_use]\n    pub fn index_buffer(&self) -> Option<&[u32]> {\n        self.index_buffer.as_deref()\n    }\n\n    /// Per-feature triangle counts produced by Earcut tessellation.\n    /// `None` unless the `GeometryValues` was created with [`Self::new_tessellated`].\n    #[must_use]\n    pub fn triangles(&self) -> Option<&[u32]> {\n        self.triangles.as_deref()\n    }\n\n    /// Flat vertex buffer: `[x0, y0, x1, y1, …]` in tile coordinates.\n    #[must_use]\n    pub fn vertices(&self) -> Option<&[i32]> {\n        self.vertices.as_deref()\n    }\n\n    /// Build a `GeoJSON` geometry for a single feature at index `i`.\n    /// Polygon and `MultiPolygon` rings are closed per `GeoJSON` spec\n    /// (MLT omits the closing vertex).\n    pub fn to_geojson(&self, index: usize) -> MltResult<Geometry<i32>> {\n        let verts = self.vertices.as_deref().unwrap_or(&[]);\n        let geoms = self.geometry_offsets.as_deref();\n        let parts = self.part_offsets.as_deref();\n        let rings = self.ring_offsets.as_deref();\n\n        let off = |s: &[u32], idx: usize, field: &'static str| -> MltResult<usize> {\n            s.get(idx)\n                .map(|&v| v.as_usize())\n                .ok_or(GeometryOutOfBounds {\n                    index,\n                    field,\n                    idx,\n                    len: s.len(),\n                })\n        };\n        let off_pair = |s: &[u32], idx: usize, field: &'static str| -> MltResult<Range<usize>> {\n            Ok(off(s, idx, field)?..off(s, idx + 1, field)?)\n        };\n\n        let geom_off = |s: &[u32], i: usize| off(s, i, \"geometry_offsets\");\n        let part_off = |s: &[u32], i: usize| off(s, i, \"part_offsets\");\n        let ring_off = |s: &[u32], i: usize| off(s, i, \"ring_offsets\");\n        let geom_range = |s: &[u32], i: usize| off_pair(s, i, \"geometry_offsets\");\n        let part_range = |s: &[u32], i: usize| off_pair(s, i, \"part_offsets\");\n        let ring_range = |s: &[u32], i: usize| off_pair(s, i, \"ring_offsets\");\n\n        let vert = |idx: usize| -> MltResult<Coord<i32>> {\n            verts\n                .get(idx * 2..idx * 2 + 2)\n                .map(|s| Coord { x: s[0], y: s[1] })\n                .ok_or(GeometryVertexOutOfBounds {\n                    index,\n                    vertex: idx,\n                    count: verts.len() / 2,\n                })\n        };\n        let line = |r: Range<usize>| -> MltResult<LineString<i32>> { r.map(&vert).collect() };\n        let closed_ring = |r: Range<usize>| -> MltResult<LineString<i32>> {\n            let first = r.start;\n            let mut coords: Vec<Coord<i32>> = r.map(&vert).collect::<Result<_, _>>()?;\n            coords.push(vert(first)?);\n            Ok(LineString(coords))\n        };\n        let poly_from_rings = |part_rng: Range<usize>, r: &[u32]| -> MltResult<Polygon<i32>> {\n            let mut rings = part_rng\n                .map(|idx| closed_ring(ring_range(r, idx)?))\n                .collect::<Result<Vec<_>, _>>()?\n                .into_iter();\n            Ok(Polygon::new(\n                rings.next().unwrap_or_else(|| LineString(vec![])),\n                rings.collect(),\n            ))\n        };\n\n        let geom_type = *self\n            .vector_types\n            .get(index)\n            .ok_or(GeometryIndexOutOfBounds(index))?;\n\n        match geom_type {\n            GeometryType::Point => {\n                // Resolve through hierarchy: geoms? -> parts? -> rings? -> vertex\n                let idx = geoms.map_or(Ok(index), |g| geom_off(g, index))?;\n                let idx = parts.map_or(Ok(idx), |p| part_off(p, idx))?;\n                let idx = rings.map_or(Ok(idx), |r| ring_off(r, idx))?;\n                Ok(Geometry::<i32>::Point(Point(vert(idx)?)))\n            }\n            GeometryType::LineString => {\n                let parts = parts.ok_or(NoPartOffsets(index, geom_type))?;\n                // Get part index: use geoms[index] if present, else index directly\n                let part_idx = geoms.map_or(Ok(index), |geom| geom_off(geom, index))?;\n                // With rings: parts[part_idx] gives ring index, use ring_offsets for vertex range\n                // Without rings: use part_offsets directly for vertex range\n                let vert_range = match rings {\n                    Some(ring) => ring_range(ring, part_off(parts, part_idx)?)?,\n                    None => part_range(parts, part_idx)?,\n                };\n                line(vert_range).map(Geometry::<i32>::LineString)\n            }\n            GeometryType::Polygon => {\n                let parts = parts.ok_or(NoPartOffsets(index, geom_type))?;\n                let rings = rings.ok_or(NoRingOffsets(index, geom_type))?;\n                let idx = geoms\n                    .map(|geom| geom_off(geom, index))\n                    .transpose()?\n                    .unwrap_or(index);\n                poly_from_rings(part_range(parts, idx)?, rings).map(Geometry::<i32>::Polygon)\n            }\n            GeometryType::MultiPoint => {\n                let geoms = geoms.ok_or(NoGeometryOffsets(index, geom_type))?;\n                let geom_rng = geom_range(geoms, index)?;\n                // Resolve vertex index through parts?->rings? hierarchy\n                // When ring_offsets exist (polygon geometry present), geometry_offsets indexes\n                // into part_offsets which indexes into ring_offsets for vertex indices.\n                // When only part_offsets exist, geometry_offsets indexes into part_offsets\n                // which gives direct vertex indices.\n                // When neither exist, geometry_offsets gives direct vertex indices.\n                let coords: Result<Vec<_>, _> = match (parts, rings) {\n                    (Some(parts), Some(rings)) => geom_rng\n                        .map(|idx| vert(ring_off(rings, part_off(parts, idx)?)?))\n                        .collect(),\n                    (Some(part), None) => geom_rng.map(|idx| vert(part_off(part, idx)?)).collect(),\n                    (None, _) => geom_rng.map(&vert).collect(),\n                };\n                Ok(Geometry::<i32>::MultiPoint(MultiPoint(\n                    coords?.into_iter().map(Point).collect(),\n                )))\n            }\n            GeometryType::MultiLineString => {\n                let geoms = geoms.ok_or(NoGeometryOffsets(index, geom_type))?;\n                let parts = parts.ok_or(NoPartOffsets(index, geom_type))?;\n                let geom_rng = geom_range(geoms, index)?;\n                // geometry_offsets indexes into part_offsets for each linestring.\n                // When ring_offsets exist (polygon geometry present), part_offsets indexes\n                // into ring_offsets for vertex ranges. Otherwise, part_offsets directly\n                // gives vertex ranges.\n                let lines: Result<Vec<_>, _> = match rings {\n                    Some(ring) => geom_rng\n                        .map(|idx| line(ring_range(ring, part_off(parts, idx)?)?))\n                        .collect(),\n                    None => geom_rng.map(|idx| line(part_range(parts, idx)?)).collect(),\n                };\n                Ok(Geometry::<i32>::MultiLineString(MultiLineString(lines?)))\n            }\n            GeometryType::MultiPolygon => {\n                let geoms = geoms.ok_or(NoGeometryOffsets(index, geom_type))?;\n                let parts = parts.ok_or(NoPartOffsets(index, geom_type))?;\n                let rings = rings.ok_or(NoRingOffsets(index, geom_type))?;\n                let polys: Vec<_> = geom_range(geoms, index)?\n                    .map(|idx| poly_from_rings(part_range(parts, idx)?, rings))\n                    .collect::<Result<_, _>>()?;\n                Ok(Geometry::<i32>::MultiPolygon(MultiPolygon(polys)))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/geometry/mod.rs",
    "content": "pub(crate) mod decode;\nmod geotype;\nmod model;\n\npub use model::*;\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/geometry/model.rs",
    "content": "use derive_debug::Dbg;\nuse num_enum::TryFromPrimitive;\nuse serde::{Deserialize, Serialize};\n\nuse crate::decoder::RawStream;\nuse crate::utils::formatter::{opt_vec_seq, vec_seq};\nuse crate::{DecodeState, Lazy};\n\n/// Geometry column representation, parameterized by decode state.\n///\n/// - `Geometry<'a>` / `Geometry<'a, Lazy>` — either raw bytes or decoded, in an [`crate::LazyParsed`] enum.\n/// - `Geometry<'a, Parsed>` — decoded [`GeometryValues`] directly (no enum wrapper).\npub type Geometry<'a, S = Lazy> = <S as DecodeState>::LazyOrParsed<RawGeometry<'a>, GeometryValues>;\n\n/// Raw geometry data as read directly from the tile (borrows from input bytes)\n#[derive(Debug, PartialEq, Clone)]\npub struct RawGeometry<'a> {\n    pub(crate) meta: RawStream<'a>,\n    pub(crate) items: Vec<RawStream<'a>>,\n}\n\n/// Parsed (decoded) geometry data\n#[derive(Clone, Dbg, Default, PartialEq, Eq)]\npub struct GeometryValues {\n    #[dbg(formatter = \"vec_seq\")]\n    pub(crate) vector_types: Vec<GeometryType>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) geometry_offsets: Option<Vec<u32>>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) part_offsets: Option<Vec<u32>>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) ring_offsets: Option<Vec<u32>>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) index_buffer: Option<Vec<u32>>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) triangles: Option<Vec<u32>>,\n    #[dbg(formatter = \"opt_vec_seq\")]\n    pub(crate) vertices: Option<Vec<i32>>,\n}\n\n/// Types of geometries supported in MLT\n#[derive(\n    Debug,\n    Clone,\n    Copy,\n    PartialEq,\n    PartialOrd,\n    Eq,\n    Hash,\n    Ord,\n    TryFromPrimitive,\n    strum::Display,\n    strum::IntoStaticStr,\n    Serialize,\n    Deserialize,\n)]\n#[repr(u8)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\npub enum GeometryType {\n    /*\n        ATTENTION: Do not modify the order of this enum - it is being used in geometry decoding\n    */\n    Point,\n    LineString,\n    Polygon,\n    MultiPoint,\n    MultiLineString,\n    MultiPolygon,\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/id/decode.rs",
    "content": "use crate::decoder::{ParsedId, RawId, RawIdValue};\nuse crate::utils::decode_presence;\nuse crate::{Decode, Decoder, MltResult};\n\nimpl<'a> Decode<ParsedId<'a>> for RawId<'a> {\n    /// Decode into a [`ParsedId`], charging `dec` before each allocation.\n    fn decode(self, dec: &mut Decoder) -> MltResult<ParsedId<'a>> {\n        let RawId { presence, value } = self;\n\n        let values: Vec<u64> = match value {\n            RawIdValue::Id32(stream) => {\n                // FIXME: ParsedId should be an enum of u32 or u64 to avoid extra allocation\n                let ids = stream.decode_u32s(dec)?;\n                dec.consume_items::<u64>(ids.len())?;\n                ids.into_iter().map(u64::from).collect()\n            }\n            RawIdValue::Id64(stream) => stream.decode_u64s(dec)?,\n        };\n\n        Ok(ParsedId(decode_presence(presence, values, dec)?))\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/id/mod.rs",
    "content": "mod decode;\nmod model;\n\npub use model::*;\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/id/model.rs",
    "content": "use std::ops::Deref;\n\nuse crate::decoder::{RawPresence, RawStream};\nuse crate::utils::Presence;\nuse crate::utils::analyze::AnalyzeViaDeref;\nuse crate::{DecodeState, Lazy};\n\n/// ID column representation, parameterized by decode state.\n///\n/// - `Id<'a>` / `Id<'a, Lazy>` — either raw bytes or decoded, in a [`crate::LazyParsed`] enum.\n/// - `Id<'a, Parsed>` — decoded [`ParsedId`] directly (no enum wrapper).\npub type Id<'a, S = Lazy> = <S as DecodeState>::LazyOrParsed<RawId<'a>, ParsedId<'a>>;\n\n/// Unparsed ID data as read directly from the tile (borrows from input bytes)\n#[derive(Debug, PartialEq, Clone)]\npub struct RawId<'a> {\n    pub(crate) presence: RawPresence<'a>,\n    pub(crate) value: RawIdValue<'a>,\n}\n\n/// A sequence of raw ID values, either 32-bit or 64-bit unsigned integers\n#[derive(Debug, PartialEq, Clone)]\npub enum RawIdValue<'a> {\n    Id32(RawStream<'a>),\n    Id64(RawStream<'a>),\n}\n\n/// Decoded ID column.\n///\n/// A transparent type over [`Presence<'a, u64>`]. All feature-access methods\n/// (`get`, `feature_count`, `dense_values`, `materialize`, `is_present`) are\n/// available via auto-deref.\n///\n/// The lifetime `'a` allows zero-copy decoding when the inner bitvector borrows\n/// from the source bytes. When the presence stream is RLE-decompressed, the inner\n/// `Cow` becomes owned and no longer borrows from the input.\n// TODO: consider converting ParsedId to an enum with u32 vs u64 for performance\n#[derive(Clone, Debug, PartialEq)]\npub struct ParsedId<'a>(pub(crate) Presence<'a, u64>);\n\nimpl<'a> Deref for ParsedId<'a> {\n    type Target = Presence<'a, u64>;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl AnalyzeViaDeref for ParsedId<'_> {}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/iterators.rs",
    "content": "//! Zero-copy per-feature view into a fully-decoded [`Layer01<Parsed>`].\n//!\n//! [`ParsedLayer01::iter_features`] yields one [`FeatureRef`] per feature via\n//! [`LendingIterator`].  [`FeatureRef::iter_properties`] exposes per-feature\n//! property values as flat [`ColumnRef`] items; `SharedDict` columns are\n//! transparently expanded and null values are skipped.\n//!\n//! # Iterator model\n//!\n//! Feature iteration uses [`LendingIterator`] rather than [`std::iter::Iterator`].\n//! This allows the iterator to reuse an internal buffer across steps — the\n//! [`FeatureRef`] borrows its property values from that buffer — eliminating a\n//! per-feature `Vec` allocation.\n//!\n//! The consequence is that each [`FeatureRef`] must be dropped before calling\n//! [`LendingIterator::next`] again, so standard adapters like `.map()` and\n//! `.collect()` are **not** available directly.  Use a `while let` loop instead:\n\nuse std::fmt;\n\nuse geo_types::Geometry;\n\nuse crate::decoder::{Layer01, ParsedLayer01, ParsedProperty, ParsedScalar, RawProperty};\nuse crate::{Lazy, LazyParsed, MltResult, Parsed};\n\n/// A minimal lending (streaming) iterator trait.\n///\n/// Unlike [`std::iter::Iterator`], the item type may borrow from the iterator\n/// itself, enabling zero-allocation iteration where the inner buffer is reused\n/// across steps.\n///\n/// Use a `while let` loop to drive the iterator:\n/// ```ignore\n/// let mut iter = layer.iter_features();\n/// while let Some(feat) = iter.next() {\n///     let feat = feat?;\n///     /* use feat here — it borrows from iter */\n/// }\n/// ```\npub trait LendingIterator {\n    /// The type of each element, which may borrow from `self`.\n    type Item<'this>\n    where\n        Self: 'this;\n\n    /// Advance the iterator, returning the next element or `None` when exhausted.\n    fn next(&mut self) -> Option<Self::Item<'_>>;\n}\n\nimpl<'a> Layer01<'a, Lazy> {\n    /// Iterate over the property column names of this layer, in order.\n    ///\n    /// Regular columns yield one [`PropName`]; `SharedDict` columns yield one name per\n    /// sub-item.  Names are available even before any column data has been decoded.\n    ///\n    /// Pair with [`FeatureRef::iter_all_properties`] to associate per-feature\n    /// values with their column names.\n    pub fn iterate_prop_names(&self) -> impl Iterator<Item = PropName<'a>> + '_ {\n        let props = &self.properties;\n        let mut col_idx = 0;\n        let mut dict_idx = 0;\n        std::iter::from_fn(move || {\n            loop {\n                let idx = col_idx;\n                col_idx += 1;\n                let name = match props.get(idx)? {\n                    LazyParsed::Raw(r) => raw_col_name(r, &mut dict_idx),\n                    LazyParsed::Parsed(p) => parsed_col_name(p, &mut dict_idx),\n                    LazyParsed::ParsingFailed => None,\n                };\n                if dict_idx != 0 {\n                    col_idx -= 1;\n                }\n                if let Some(n) = name {\n                    return Some(n);\n                }\n            }\n        })\n    }\n}\n\nimpl<'a> ParsedLayer01<'a> {\n    /// Iterate over all features in this fully-decoded layer via a [`LendingIterator`].\n    ///\n    /// Yields one `MltResult<`[`FeatureRef`]`>` per feature. Geometry decoding can\n    /// fail, hence the `Result` wrapper.\n    ///\n    /// ```text\n    /// let mut iter = parsed.iter_features();\n    /// while let Some(feat) = iter.next() {\n    ///     let feat = feat?;\n    ///     for col in feat.iter_properties() {\n    ///        // or use iter_all_properties() to include Nones\n    ///     }\n    /// }\n    /// ```\n    ///\n    /// All inner iterators — [`FeatureRef::iter_properties`],\n    /// [`FeatureRef::iter_all_properties`], and the name iterators — implement the\n    /// standard [`std::iter::Iterator`] trait and compose normally.\n    #[must_use]\n    pub fn iter_features(&self) -> Layer01FeatureIter<'_, 'a> {\n        Layer01FeatureIter::new(self)\n    }\n\n    /// Iterate over the property column names of this layer, in order.\n    /// See [`Layer01::iterate_prop_names`] for details.\n    pub fn iterate_prop_names(&self) -> impl Iterator<Item = PropName<'a>> + '_ {\n        Layer01PropNamesIter::new(&self.properties)\n    }\n}\n\n/// A zero-allocation two-part property name yielded by [`FeatureRef::iter_properties`].\n///\n/// The two parts concatenate on [`Display`](fmt::Display) as `\"{}{}\"`:\n/// - For regular columns: `(column_name, \"\")` — zero allocation, second part always empty.\n/// - For `SharedDict` sub-items: `(prefix, suffix)` — both borrow directly from layer data.\n///\n/// Structural [`PartialEq`] compares both parts independently.  Use [`PartialEq<str>`] or\n/// [`PartialEq<&str>`] (also implemented) to compare against a plain `&str` as if the two\n/// parts were concatenated.\n#[derive(Debug, Clone, Copy)] // WARN: do not auto-derive PartialEq,Eq,Hash as it won't be correct\npub struct PropName<'a>(&'a str, &'a str);\n\nimpl fmt::Display for PropName<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(self.0)?;\n        f.write_str(self.1)\n    }\n}\n\nimpl PartialEq<PropName<'_>> for PropName<'_> {\n    fn eq(&self, other: &PropName<'_>) -> bool {\n        // Compare the concatenated strings byte-by-byte without allocating.\n        let (a0, a1) = (self.0.as_bytes(), self.1.as_bytes());\n        let a = a0.iter().chain(a1);\n        let (b0, b1) = (other.0.as_bytes(), other.1.as_bytes());\n        let b = b0.iter().chain(b1);\n        let combined_len_eq = a0.len() + a1.len() == b0.len() + b1.len();\n        combined_len_eq && a.eq(b)\n    }\n}\n\nimpl PartialEq<str> for PropName<'_> {\n    /// Returns `true` if `other == self.0 + self.1`.\n    fn eq(&self, other: &str) -> bool {\n        other.strip_prefix(self.0) == Some(self.1)\n    }\n}\n\nimpl PartialEq<PropName<'_>> for str {\n    fn eq(&self, other: &PropName<'_>) -> bool {\n        other == self\n    }\n}\n\nimpl PartialEq<&str> for PropName<'_> {\n    fn eq(&self, other: &&str) -> bool {\n        self == *other\n    }\n}\n\nimpl PartialEq<PropName<'_>> for &str {\n    fn eq(&self, other: &PropName<'_>) -> bool {\n        other == *self\n    }\n}\n\n/// A borrowed, non-null per-feature property value.\n///\n/// Nullability is lifted to [`ColumnRef`]: only non-null values appear in\n/// [`FeatureRef::iter_properties`].\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum PropValueRef<'a> {\n    Bool(bool),\n    I8(i8),\n    U8(u8),\n    I32(i32),\n    U32(u32),\n    I64(i64),\n    U64(u64),\n    F32(f32),\n    F64(f64),\n    Str(&'a str),\n}\n\nmacro_rules! impl_from_for_prop_value_ref {\n    ($($ty:ty => $variant:ident),+ $(,)?) => {\n        $(impl From<$ty> for PropValueRef<'_> {\n            fn from(v: $ty) -> Self { Self::$variant(v) }\n        })+\n    };\n}\nimpl_from_for_prop_value_ref!(\n    bool => Bool, i8 => I8, u8 => U8,\n    i32 => I32, u32 => U32,\n    i64 => I64, u64 => U64,\n    f32 => F32, f64 => F64,\n);\n\n/// A single non-null property value for one feature, yielded by [`FeatureRef::iter_properties`].\n///\n/// `name` is a [`PropName`] that displays as `\"{prefix}{suffix}\"`.\n/// All borrows are zero-copy from the layer data.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct ColumnRef<'a> {\n    pub name: PropName<'a>,\n    pub value: PropValueRef<'a>,\n}\n\n/// A single map feature returned by [`ParsedLayer01::iter_features`].\n///\n/// Borrows `values` from the outer [`Layer01FeatureIter`] buffer — it must be\n/// dropped before calling [`LendingIterator::next`] again.\n#[derive(Debug)]\npub struct FeatureRef<'feat, 'layer: 'feat> {\n    /// Optional feature ID.\n    pub id: Option<u64>,\n    /// Geometry in [`Geometry<i32>`] form (owned, decoded on demand by the iterator).\n    pub geometry: Geometry<i32>,\n    /// Borrowed slice of column descriptors from the layer; used to yield column names.\n    columns: &'layer [ParsedProperty<'layer>],\n    /// Per-feature values in column order, one per slot (scalar, string, or `SharedDict`\n    /// sub-item).  Borrowed from the iterator's reused buffer — no allocation per feature.\n    values: &'feat [Option<PropValueRef<'layer>>],\n}\n\nimpl<'feat, 'layer: 'feat> FeatureRef<'feat, 'layer> {\n    /// Iterate over every property slot for this feature, **values only**, in column order.\n    ///\n    /// Yields `Option<PropValueRef>`:\n    /// - `Some(value)` — the slot contains a non-null value.\n    /// - `None` — the slot is null / absent.\n    ///\n    /// Use [`Layer01::iterate_prop_names`] to pair values with their column names.\n    pub fn iter_all_properties(&self) -> impl Iterator<Item = Option<PropValueRef<'layer>>> + '_ {\n        self.values.iter().copied()\n    }\n\n    /// Iterate over all non-null properties for this feature.\n    ///\n    /// `SharedDict` columns are transparently expanded into one [`ColumnRef`] per sub-item.\n    /// Null / absent values are skipped entirely. The iterator is infallible.\n    pub fn iter_properties(&self) -> impl Iterator<Item = ColumnRef<'layer>> + '_ {\n        Layer01PropNamesIter::new(self.columns)\n            .zip(self.values.iter().copied())\n            .filter_map(|(name, opt_val)| opt_val.map(|value| ColumnRef { name, value }))\n    }\n\n    /// Look up a property by name, returning its value if present and non-null.\n    ///\n    /// For `SharedDict` columns the expected name is `\"{prefix}{suffix}\"`, matching\n    /// the key used by [`iter_properties`](Self::iter_properties).\n    #[must_use]\n    pub fn get_property(&self, name: &str) -> Option<PropValueRef<'layer>> {\n        self.iter_properties()\n            .find(|col| col.name == name)\n            .map(|col| col.value)\n    }\n}\n\n// ── Column name helpers ───────────────────────────────────────────────────────\n\n/// Iterates the property column names of a fully-decoded [`ParsedLayer01`].\n///\n/// Regular columns yield one [`PropName`]; `SharedDict` columns yield one name per\n/// sub-item (`(prefix, suffix)`).\npub(crate) struct Layer01PropNamesIter<'a, 'p> {\n    props: &'a [ParsedProperty<'p>],\n    col_idx: usize,\n    dict_idx: usize,\n}\n\nimpl<'a, 'p> Layer01PropNamesIter<'a, 'p> {\n    pub(crate) fn new(props: &'a [ParsedProperty<'p>]) -> Self {\n        Self {\n            props,\n            col_idx: 0,\n            dict_idx: 0,\n        }\n    }\n}\n\nimpl<'a> Iterator for Layer01PropNamesIter<'_, 'a> {\n    type Item = PropName<'a>;\n\n    fn next(&mut self) -> Option<PropName<'a>> {\n        loop {\n            let col_idx = self.col_idx;\n            self.col_idx += 1;\n            let name = parsed_col_name(self.props.get(col_idx)?, &mut self.dict_idx);\n            if self.dict_idx != 0 {\n                self.col_idx -= 1; // SharedDict not yet exhausted: revisit this column\n            }\n            if let Some(n) = name {\n                return Some(n);\n            }\n        }\n    }\n}\n\n/// Yield the next [`PropName`] from a [`ParsedProperty`] column.\n#[inline]\nfn parsed_col_name<'p>(prop: &ParsedProperty<'p>, dict_idx: &mut usize) -> Option<PropName<'p>> {\n    use ParsedProperty as P;\n    match prop {\n        P::Bool(s) => Some(PropName(s.name, \"\")),\n        P::I8(s) => Some(PropName(s.name, \"\")),\n        P::U8(s) => Some(PropName(s.name, \"\")),\n        P::I32(s) => Some(PropName(s.name, \"\")),\n        P::U32(s) => Some(PropName(s.name, \"\")),\n        P::I64(s) => Some(PropName(s.name, \"\")),\n        P::U64(s) => Some(PropName(s.name, \"\")),\n        P::F32(s) => Some(PropName(s.name, \"\")),\n        P::F64(s) => Some(PropName(s.name, \"\")),\n        P::Str(s) => Some(PropName(s.name, \"\")),\n        P::SharedDict(sd) => {\n            if *dict_idx < sd.items.len() {\n                let idx = *dict_idx;\n                *dict_idx += 1;\n                Some(PropName(sd.prefix, sd.items[idx].suffix))\n            } else {\n                *dict_idx = 0;\n                None\n            }\n        }\n    }\n}\n\n/// Yield the next [`PropName`] from a [`RawProperty`] column.  See [`parsed_col_name`].\n#[inline]\nfn raw_col_name<'p>(prop: &RawProperty<'p>, dict_idx: &mut usize) -> Option<PropName<'p>> {\n    use RawProperty as P;\n    match prop {\n        P::Bool(s)\n        | P::I8(s)\n        | P::U8(s)\n        | P::I32(s)\n        | P::U32(s)\n        | P::I64(s)\n        | P::U64(s)\n        | P::F32(s)\n        | P::F64(s) => Some(PropName(s.name, \"\")),\n        P::Str(s) => Some(PropName(s.name, \"\")),\n        P::SharedDict(sd) => {\n            if *dict_idx < sd.children.len() {\n                let idx = *dict_idx;\n                *dict_idx += 1;\n                Some(PropName(sd.name, sd.children[idx].name))\n            } else {\n                *dict_idx = 0;\n                None\n            }\n        }\n    }\n}\n\n/// A boxed per-column-slot value iterator yielding one `Option<`[`PropValueRef`]`>` per feature.\ntype ColValIter<'l> = Box<dyn Iterator<Item = Option<PropValueRef<'l>>> + 'l>;\n\n/// Build one [`ColValIter`] per property column \"slot\" from a decoded column slice.\n///\n/// - Scalar and string columns contribute one slot each.\n/// - `SharedDict` columns contribute one slot per sub-item.\nfn build_col_iters<'p>(columns: &'p [ParsedProperty<'p>]) -> Vec<ColValIter<'p>> {\n    use ParsedProperty as PP;\n    let mut iters: Vec<ColValIter<'p>> = Vec::new();\n    for col in columns {\n        match col {\n            PP::Bool(s) => iters.push(scalar_col_iter(s)),\n            PP::I8(s) => iters.push(scalar_col_iter(s)),\n            PP::U8(s) => iters.push(scalar_col_iter(s)),\n            PP::I32(s) => iters.push(scalar_col_iter(s)),\n            PP::U32(s) => iters.push(scalar_col_iter(s)),\n            PP::I64(s) => iters.push(scalar_col_iter(s)),\n            PP::U64(s) => iters.push(scalar_col_iter(s)),\n            PP::F32(s) => iters.push(scalar_col_iter(s)),\n            PP::F64(s) => iters.push(scalar_col_iter(s)),\n            PP::Str(strings) => {\n                let data: &'p str = strings.data.as_ref();\n                let lengths: &'p [i32] = &strings.lengths;\n                let mut curr_end: u32 = 0;\n                let mut feat_idx = 0usize;\n                iters.push(Box::new(std::iter::from_fn(move || {\n                    let &end_i32 = lengths.get(feat_idx)?;\n                    feat_idx += 1;\n                    if end_i32 >= 0 {\n                        let start = curr_end as usize;\n                        curr_end = end_i32.cast_unsigned();\n                        Some(data.get(start..curr_end as usize).map(PropValueRef::Str))\n                    } else {\n                        // Null slot: curr_end unchanged (null encodes the current byte offset).\n                        Some(None)\n                    }\n                })));\n            }\n            PP::SharedDict(dict) => {\n                for item in &dict.items {\n                    let dict_ref: &'p _ = dict;\n                    let item_ref: &'p _ = item;\n                    let mut feat_idx = 0usize;\n                    iters.push(Box::new(std::iter::from_fn(move || {\n                        if feat_idx >= item_ref.ranges.len() {\n                            return None;\n                        }\n                        let idx = feat_idx;\n                        feat_idx += 1;\n                        Some(item_ref.get(dict_ref, idx).map(PropValueRef::Str))\n                    })));\n                }\n            }\n        }\n    }\n    iters\n}\n\n/// Build a boxed value iterator for a single scalar property column.\nfn scalar_col_iter<'p, T>(scalar: &'p ParsedScalar<'p, T>) -> ColValIter<'p>\nwhere\n    T: Copy + PartialEq,\n    PropValueRef<'p>: From<T>,\n{\n    Box::new(scalar.iter_optional().map(|o| o.map(PropValueRef::from)))\n}\n\n/// Iterator over the features of a fully-decoded [`Layer01<Parsed>`].\n///\n/// Returned by [`ParsedLayer01::iter_features`]. Implements [`LendingIterator`]:\n/// advance with `while let Some(feat) = iter.next()`.\n///\n/// Holds one O(1)-per-step cursor per property column slot. On each step the\n/// per-column cursors are advanced and their results written into a reused\n/// `values_buf` — yielding a [`FeatureRef`] that borrows that buffer with no\n/// per-feature heap allocation.\npub struct Layer01FeatureIter<'layer, 'data: 'layer> {\n    layer: &'layer Layer01<'data, Parsed>,\n    index: usize,\n    feature_count: usize,\n    /// ID iterator, `None` when the layer has no ID column.\n    id_iter: Option<crate::utils::PresenceOptIter<'layer, u64>>,\n    /// One boxed value iterator per column slot (scalar, string, or `SharedDict` sub-item).\n    col_iters: Vec<ColValIter<'layer>>,\n    /// Reused buffer: filled on each `next()` call, borrowed by the yielded [`FeatureRef`].\n    values_buf: Vec<Option<PropValueRef<'layer>>>,\n}\n\nimpl<'layer, 'data: 'layer> Layer01FeatureIter<'layer, 'data> {\n    fn new(layer: &'layer Layer01<'data, Parsed>) -> Self {\n        let col_iters = build_col_iters(&layer.properties);\n        let cap = col_iters.len();\n        Self {\n            layer,\n            index: 0,\n            feature_count: layer.feature_count(),\n            id_iter: layer.id.as_ref().map(|id| id.iter_optional()),\n            col_iters,\n            values_buf: Vec::with_capacity(cap),\n        }\n    }\n\n    /// Number of features not yet yielded.\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.feature_count - self.index\n    }\n\n    /// Returns `true` if all features have been yielded.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.index >= self.feature_count\n    }\n}\n\nimpl<'layer> LendingIterator for Layer01FeatureIter<'layer, '_> {\n    type Item<'this>\n        = MltResult<FeatureRef<'this, 'layer>>\n    where\n        Self: 'this;\n\n    fn next(&mut self) -> Option<Self::Item<'_>> {\n        let index = self.index;\n        if index >= self.feature_count {\n            return None;\n        }\n        self.index += 1;\n\n        // Advance all per-feature cursors unconditionally, even if geometry decode fails,\n        // so that IDs and property values remain aligned with geometry indices.\n        let id = self.id_iter.as_mut().and_then(Iterator::next).flatten();\n        self.values_buf.clear();\n        self.values_buf\n            .extend(self.col_iters.iter_mut().map(|it| it.next().flatten()));\n\n        Some(\n            self.layer\n                .geometry\n                .to_geojson(index)\n                .map(|geometry| FeatureRef {\n                    id,\n                    geometry,\n                    columns: &self.layer.properties,\n                    values: &self.values_buf,\n                }),\n        )\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use geo_types::Point;\n    use serde_json::Value;\n\n    use super::*;\n    use crate::Layer;\n    use crate::decoder::GeometryValues;\n    use crate::encoder::model::StagedLayer;\n    use crate::encoder::{Codecs, Encoder, Presence, StagedId, StagedProperty, StagedSharedDict};\n    use crate::test_helpers::{dec, parser};\n\n    fn layer_buf(staged: StagedLayer) -> Vec<u8> {\n        staged\n            .encode_into(Encoder::default(), &mut Codecs::default())\n            .unwrap()\n            .into_layer_bytes()\n            .unwrap()\n    }\n\n    fn three_points() -> GeometryValues {\n        let mut g = GeometryValues::default();\n        g.push_geom(&Geometry::<i32>::Point(Point::new(1, 2)));\n        g.push_geom(&Geometry::<i32>::Point(Point::new(3, 4)));\n        g.push_geom(&Geometry::<i32>::Point(Point::new(5, 6)));\n        g\n    }\n\n    fn empty_layer(name: &str) -> StagedLayer {\n        StagedLayer {\n            name: name.to_string(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: GeometryValues::default(),\n            properties: vec![],\n        }\n    }\n\n    #[test]\n    fn prop_name_display_concatenates_parts() {\n        assert_eq!(PropName(\"addr:\", \"city\").to_string(), \"addr:city\");\n        assert_eq!(PropName(\"name\", \"\").to_string(), \"name\");\n        assert_eq!(PropName(\"\", \"\").to_string(), \"\");\n    }\n\n    #[test]\n    fn prop_name_eq_str_matches_concatenation() {\n        assert_eq!(PropName(\"addr:\", \"city\"), \"addr:city\");\n        assert_eq!(\"addr:city\", PropName(\"addr:\", \"city\"));\n        assert_ne!(PropName(\"addr:\", \"city\"), \"addr:\");\n        assert_ne!(PropName(\"addr:\", \"city\"), \"city\");\n        assert_eq!(PropName(\"name\", \"\"), \"name\");\n    }\n\n    #[test]\n    fn prop_name_structural_eq_is_part_wise() {\n        assert_eq!(PropName(\"a\", \"b\"), PropName(\"a\", \"b\"));\n        assert_eq!(PropName(\"ab\", \"\"), PropName(\"a\", \"b\"));\n    }\n\n    #[test]\n    fn prop_name_eq_prop_name_semantic_equality() {\n        assert_eq!(PropName(\"ab\", \"\"), PropName(\"a\", \"b\"));\n        assert_eq!(PropName(\"\", \"ab\"), PropName(\"a\", \"b\"));\n        assert_eq!(PropName(\"abc\", \"def\"), PropName(\"ab\", \"cdef\"));\n        assert_eq!(PropName(\"a\", \"bcdef\"), PropName(\"abcde\", \"f\"));\n\n        assert_ne!(PropName(\"a\", \"b\"), PropName(\"a\", \"c\"));\n        assert_ne!(PropName(\"a\", \"b\"), PropName(\"ab\", \"c\"));\n        assert_ne!(PropName(\"abc\", \"\"), PropName(\"ab\", \"\"));\n    }\n\n    #[test]\n    fn prop_value_ref_scalars_convert_to_json() {\n        assert_eq!(Value::from(PropValueRef::Bool(true)), Value::Bool(true));\n        assert_eq!(Value::from(PropValueRef::Bool(false)), Value::Bool(false));\n        assert_eq!(Value::from(PropValueRef::I8(-1)), Value::from(-1_i8));\n        assert_eq!(Value::from(PropValueRef::U8(255)), Value::from(255_u8));\n        assert_eq!(\n            Value::from(PropValueRef::I32(-1000)),\n            Value::from(-1000_i32)\n        );\n        assert_eq!(Value::from(PropValueRef::U32(1000)), Value::from(1000_u32));\n        assert_eq!(\n            Value::from(PropValueRef::I64(i64::MIN)),\n            Value::from(i64::MIN)\n        );\n        assert_eq!(\n            Value::from(PropValueRef::U64(u64::MAX)),\n            Value::from(u64::MAX)\n        );\n        assert_eq!(\n            Value::from(PropValueRef::Str(\"hello\")),\n            Value::String(\"hello\".into())\n        );\n    }\n\n    #[test]\n    fn prop_value_ref_float_finite_is_number() {\n        assert!(matches!(\n            Value::from(PropValueRef::F32(1.5)),\n            Value::Number(_)\n        ));\n        assert!(matches!(\n            Value::from(PropValueRef::F64(2.5)),\n            Value::Number(_)\n        ));\n    }\n\n    #[test]\n    fn prop_value_ref_float_non_finite_becomes_string_sentinel() {\n        assert_eq!(\n            Value::from(PropValueRef::F32(f32::NAN)),\n            Value::String(\"f32::NAN\".into())\n        );\n        assert_eq!(\n            Value::from(PropValueRef::F32(f32::INFINITY)),\n            Value::String(\"f32::INFINITY\".into())\n        );\n        assert_eq!(\n            Value::from(PropValueRef::F64(f64::NAN)),\n            Value::String(\"f64::NAN\".into())\n        );\n        assert_eq!(\n            Value::from(PropValueRef::F64(f64::NEG_INFINITY)),\n            Value::String(\"f64::NEG_INFINITY\".into())\n        );\n    }\n\n    #[test]\n    fn empty_layer_yields_no_features() {\n        let buf = layer_buf(empty_layer(\"empty\"));\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else {\n            panic!(\"expected Tag01\")\n        };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let iter = parsed.iter_features();\n        assert_eq!(iter.len(), 0);\n        assert!(iter.is_empty());\n        assert_eq!(parsed.iter_features().len(), 0);\n    }\n\n    #[test]\n    fn len_decreases_with_each_next() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n        assert_eq!(iter.len(), 3);\n        iter.next().unwrap().unwrap();\n        assert_eq!(iter.len(), 2);\n        iter.next().unwrap().unwrap();\n        assert_eq!(iter.len(), 1);\n        iter.next().unwrap().unwrap();\n        assert_eq!(iter.len(), 0);\n        assert!(iter.is_empty());\n        assert!(iter.next().is_none());\n    }\n\n    #[test]\n    fn feature_ids_are_preserved() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::from_optional(vec![Some(100), None, Some(200)]),\n            geometry: three_points(),\n            properties: vec![],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut ids = Vec::new();\n        let mut iter = parsed.iter_features();\n        while let Some(r) = iter.next() {\n            ids.push(r.unwrap().id);\n        }\n        assert_eq!(ids, [Some(100), None, Some(200)]);\n    }\n\n    #[test]\n    fn geometry_values_match_input() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut geoms = Vec::new();\n        let mut iter = parsed.iter_features();\n        while let Some(r) = iter.next() {\n            geoms.push(r.unwrap().geometry);\n        }\n        assert_eq!(geoms[0], Geometry::<i32>::Point(Point::new(1, 2)));\n        assert_eq!(geoms[1], Geometry::<i32>::Point(Point::new(3, 4)));\n        assert_eq!(geoms[2], Geometry::<i32>::Point(Point::new(5, 6)));\n    }\n\n    #[test]\n    fn null_scalar_values_are_skipped() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![StagedProperty::opt_u32(\"n\", vec![Some(1), None, Some(3)])],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n\n        {\n            let feat = iter.next().unwrap().unwrap();\n            let cols: Vec<_> = feat.iter_properties().collect();\n            assert_eq!(cols.len(), 1);\n            assert_eq!(cols[0].name, PropName(\"n\", \"\"));\n            assert_eq!(cols[0].name, \"n\");\n            assert_eq!(cols[0].value, PropValueRef::U32(1));\n            let all: Vec<_> = feat.iter_all_properties().collect();\n            assert_eq!(all, [Some(PropValueRef::U32(1))]);\n        }\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert!(feat.iter_properties().next().is_none());\n            let all: Vec<_> = feat.iter_all_properties().collect();\n            assert_eq!(all, [None]);\n        }\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.get_property(\"n\"), Some(PropValueRef::U32(3)));\n            let all: Vec<_> = feat.iter_all_properties().collect();\n            assert_eq!(all, [Some(PropValueRef::U32(3))]);\n        }\n\n        let names: Vec<_> = parsed.iterate_prop_names().map(|n| n.to_string()).collect();\n        assert_eq!(names, [\"n\"]);\n    }\n\n    #[test]\n    fn null_string_values_are_skipped() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![StagedProperty::opt_str(\n                \"label\",\n                vec![Some(\"foo\"), None, Some(\"bar\")],\n            )],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.get_property(\"label\"), Some(PropValueRef::Str(\"foo\")));\n        }\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.get_property(\"label\"), None);\n        }\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.get_property(\"label\"), Some(PropValueRef::Str(\"bar\")));\n        }\n    }\n\n    #[test]\n    fn multiple_columns_independently_nullable() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![\n                StagedProperty::opt_bool(\"flag\", vec![Some(true), Some(false), None]),\n                StagedProperty::opt_i32(\"score\", vec![None, Some(-5), Some(7)]),\n            ],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n\n        // feat 0: flag=true, score=null → 1 property\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.iter_properties().count(), 1);\n            assert_eq!(feat.get_property(\"flag\"), Some(PropValueRef::Bool(true)));\n            assert_eq!(feat.get_property(\"score\"), None);\n        }\n        // feat 1: flag=false, score=-5 → 2 properties\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.iter_properties().count(), 2);\n            assert_eq!(feat.get_property(\"flag\"), Some(PropValueRef::Bool(false)));\n            assert_eq!(feat.get_property(\"score\"), Some(PropValueRef::I32(-5)));\n        }\n        // feat 2: flag=null, score=7 → 1 property\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.iter_properties().count(), 1);\n            assert_eq!(feat.get_property(\"flag\"), None);\n            assert_eq!(feat.get_property(\"score\"), Some(PropValueRef::I32(7)));\n        }\n    }\n\n    #[test]\n    fn geometry_error_does_not_misalign_ids() {\n        use crate::decoder::GeometryType;\n\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::from_optional(vec![Some(10), Some(20), Some(30)]),\n            geometry: three_points(),\n            properties: vec![],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let mut parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        // Corrupt feature 1's geometry type: Point → LineString.\n        // A LineString requires part_offsets, which are absent here, so\n        // to_geojson(1) will return Err(NoPartOffsets).\n        parsed.geometry.vector_types[1] = GeometryType::LineString;\n\n        let mut iter = parsed.iter_features();\n\n        // Feature 0: valid Point, id = Some(10)\n        let feat0 = iter.next().unwrap().unwrap();\n        assert_eq!(feat0.id, Some(10));\n\n        // Feature 1: geometry error — iterator still advances ID cursor\n        assert!(iter.next().unwrap().is_err());\n\n        // Feature 2: valid Point, id must be Some(30), not Some(20)\n        let feat2 = iter.next().unwrap().unwrap();\n        assert_eq!(\n            feat2.id,\n            Some(30),\n            \"id cursor was not advanced on geometry error\"\n        );\n\n        assert!(iter.next().is_none());\n    }\n\n    #[test]\n    fn get_property_absent_column_returns_none() {\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![StagedProperty::u32(\"x\", vec![1, 2, 3])],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n        let feat = iter.next().unwrap().unwrap();\n        assert_eq!(feat.get_property(\"no_such_column\"), None);\n    }\n\n    #[test]\n    fn shared_dict_columns_are_expanded() {\n        let shared_dict = StagedSharedDict::new(\n            \"addr:\",\n            [\n                (\n                    \"city\",\n                    vec![Some(\"Paris\"), Some(\"Rome\"), None],\n                    Presence::Mixed,\n                ),\n                (\n                    \"zip\",\n                    vec![Some(\"75001\"), None, Some(\"00100\")],\n                    Presence::Mixed,\n                ),\n            ],\n        )\n        .unwrap();\n\n        let buf = layer_buf(StagedLayer {\n            name: \"test\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: three_points(),\n            properties: vec![StagedProperty::SharedDict(shared_dict)],\n        });\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let parsed = lazy.decode_all(&mut dec()).unwrap();\n\n        let mut iter = parsed.iter_features();\n\n        // feat 0: city=Paris, zip=75001\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(\n                feat.get_property(\"addr:city\"),\n                Some(PropValueRef::Str(\"Paris\"))\n            );\n            assert_eq!(\n                feat.get_property(\"addr:zip\"),\n                Some(PropValueRef::Str(\"75001\"))\n            );\n            assert_eq!(feat.iter_properties().count(), 2);\n        }\n        // feat 1: city=Rome, zip=null\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(\n                feat.get_property(\"addr:city\"),\n                Some(PropValueRef::Str(\"Rome\"))\n            );\n            assert_eq!(feat.get_property(\"addr:zip\"), None);\n            assert_eq!(feat.iter_properties().count(), 1);\n            // iter_all_properties: values only (no names); SharedDict expands to two slots\n            let all: Vec<_> = feat.iter_all_properties().collect();\n            assert_eq!(all, [Some(PropValueRef::Str(\"Rome\")), None]);\n        }\n        // feat 2: city=null, zip=00100\n        {\n            let feat = iter.next().unwrap().unwrap();\n            assert_eq!(feat.get_property(\"addr:city\"), None);\n            assert_eq!(\n                feat.get_property(\"addr:zip\"),\n                Some(PropValueRef::Str(\"00100\"))\n            );\n        }\n\n        let names: Vec<_> = parsed.iterate_prop_names().map(|n| n.to_string()).collect();\n        assert_eq!(names, [\"addr:city\", \"addr:zip\"]);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/layer.rs",
    "content": "use crate::codecs::varint::parse_varint;\nuse crate::decoder::{Layer01, Unknown};\nuse crate::utils::{parse_u8, take};\nuse crate::{DecodeState, Decoder, Layer, MltError, MltRefResult, MltResult, ParsedLayer, Parser};\n\nimpl<'a, S: DecodeState> Layer<'a, S> {\n    /// Returns the inner `Layer01` if this is a Tag01 layer, or `None` otherwise.\n    #[must_use]\n    pub fn as_layer01(&self) -> Option<&Layer01<'a, S>> {\n        match self {\n            Self::Tag01(l) => Some(l),\n            Self::Unknown(_) => None,\n        }\n    }\n\n    /// Consumes this layer and returns the inner `Layer01`, or `None` if it is not a Tag01 layer.\n    #[must_use]\n    pub fn into_layer01(self) -> Option<Layer01<'a, S>> {\n        match self {\n            Self::Tag01(l) => Some(l),\n            Self::Unknown(_) => None,\n        }\n    }\n}\n\nimpl<'a> Layer<'a> {\n    /// Parse a single tuple that consists of `size (varint)`, `tag (varint)`, and `value (bytes)`.\n    /// Reserves memory for decoded data against the parser's budget.\n    pub(crate) fn from_bytes(input: &'a [u8], parser: &mut Parser) -> MltRefResult<'a, Self> {\n        let (input, size) = parse_varint::<u32>(input)?;\n\n        // tag is a varint, but we know fewer than 127 tags for now,\n        // so we can use a faster u8 and fail if it is bigger than 127.\n        let (input, tag) = parse_u8(input)?;\n        // 1 byte must be parsed for the tag, so if size is 0, it's invalid\n        let size = size.checked_sub(1).ok_or(MltError::ZeroLayerSize)?;\n        let (input, value) = take(input, size)?;\n\n        let layer = match tag {\n            // For now, we only support tag 0x01 layers, but more will be added soon\n            1 => Layer::Tag01(Layer01::from_bytes(value, parser)?),\n            tag => Layer::Unknown(Unknown { tag, value }),\n        };\n\n        Ok((input, layer))\n    }\n\n    /// Decode all columns and return a fully-decoded [`ParsedLayer`].\n    ///\n    /// Consumes `self`.  For partial / incremental decoding, destructure with\n    /// `Layer::Tag01(lazy)` and call the individual methods on [`Layer01`].\n    pub fn decode_all(self, dec: &mut Decoder) -> MltResult<ParsedLayer<'a>> {\n        match self {\n            Layer::Tag01(lazy) => Ok(Layer::Tag01(lazy.decode_all(dec)?)),\n            Layer::Unknown(u) => Ok(Layer::Unknown(u)),\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/mod.rs",
    "content": "mod analyze;\nmod column;\n#[cfg(all(not(test), feature = \"arbitrary\"))]\npub mod fuzzing;\nmod geometry;\nmod id;\nmod iterators;\nmod layer;\nmod model;\nmod property;\nmod root;\npub(crate) mod stream;\nmod tile;\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\n// ── Crate-internal re-exports ─────────────────────────────────────────────────\n// Allow internal modules to keep using `crate::decoder::*` paths without\n// reaching into sub-module paths explicitly.\npub(crate) use geometry::{Geometry, RawGeometry};\npub use geometry::{GeometryType, GeometryValues};\npub use id::ParsedId;\n// pub (not pub(crate)) so __private module can re-export it\npub(crate) use id::{Id, RawId, RawIdValue};\npub use iterators::{\n    ColumnRef, FeatureRef, Layer01FeatureIter, LendingIterator, PropName, PropValueRef,\n};\npub(crate) use model::Column;\npub use model::{\n    ColumnType, Layer, Layer01, ParsedLayer, ParsedLayer01, PropKind, PropValue, TileFeature,\n    TileLayer, Unknown,\n};\n// Re-export strings sub-module so encoder can use `crate::decoder::strings::*`\npub(crate) use property::strings;\npub(crate) use property::{\n    DictRange, ParsedProperty, ParsedScalar, ParsedSharedDict, ParsedSharedDictItem, ParsedStrings,\n    Property, RawFsstData, RawPlainData, RawPresence, RawProperty, RawScalar, RawSharedDict,\n    RawSharedDictEncoding, RawSharedDictItem, RawStrings, RawStringsEncoding,\n};\npub use root::{Decoder, Parser};\npub(crate) use stream::model::{\n    DictionaryType, IntEncoding, LengthType, LogicalEncoding, LogicalTechnique, LogicalValue,\n    Morton, OffsetType, PhysicalEncoding, RawStream, RleMeta, StreamMeta, StreamType,\n};\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/model.rs",
    "content": "use std::fmt;\n\nuse num_enum::TryFromPrimitive;\n\nuse crate::decoder::{Geometry, Id, Property};\nuse crate::{DecodeState, Lazy, Parsed};\n\n/// A layer that can be one of the known types, or an unknown.\n///\n/// The decode-state type parameter `S` mirrors [`Layer01<'a, S>`]:\n/// - `Layer<'a>` / `Layer<'a, Lazy>` — freshly parsed; columns may still be raw bytes.\n/// - `Layer<'a, Parsed>` — returned by [`Layer::decode_all`]; all columns are decoded. Use `ParsedLayer` alias.\n#[non_exhaustive]\npub enum Layer<'a, S: DecodeState = Lazy> {\n    /// MVT-compatible layer (tag = 1)\n    Tag01(Layer01<'a, S>),\n    /// Unknown layer with tag, size, and value\n    Unknown(Unknown<'a>),\n}\npub type ParsedLayer<'a> = Layer<'a, Parsed>;\n\nimpl<'a, S: DecodeState> fmt::Debug for Layer<'a, S>\nwhere\n    Layer01<'a, S>: fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::Tag01(l) => f.debug_tuple(\"Tag01\").field(l).finish(),\n            Self::Unknown(u) => f.debug_tuple(\"Unknown\").field(u).finish(),\n        }\n    }\n}\n\n/// Unknown layer data, stored as encoded bytes.\n///\n/// Returned inside [`Layer::Unknown`] for any layer tag that is not recognized\n/// by this version of the library. Consumers can inspect the tag and raw bytes\n/// to forward or log the layer without losing data.\n#[derive(Debug, Clone, Default, PartialEq)]\npub struct Unknown<'a> {\n    pub(crate) tag: u8,\n    pub(crate) value: &'a [u8],\n}\n\nimpl<'a> Unknown<'a> {\n    /// The raw layer tag identifying this unrecognised layer type.\n    #[must_use]\n    pub fn tag(&self) -> u32 {\n        u32::from(self.tag)\n    }\n\n    /// The raw encoded bytes of this layer's body.\n    #[must_use]\n    pub fn data(&self) -> &'a [u8] {\n        self.value\n    }\n}\n\n/// Column definition\n#[derive(Debug, PartialEq)]\npub struct Column<'a> {\n    pub(crate) typ: ColumnType,\n    pub(crate) name: Option<&'a str>,\n    pub(crate) children: Vec<Self>,\n}\n\n/// Column data type, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]\n#[repr(u8)]\npub enum ColumnType {\n    Id = 0,\n    OptId = 1,\n    LongId = 2,\n    OptLongId = 3,\n    Geometry = 4,\n    Bool = 10,\n    OptBool = 11,\n    I8 = 12,\n    OptI8 = 13,\n    U8 = 14,\n    OptU8 = 15,\n    I32 = 16,\n    OptI32 = 17,\n    U32 = 18,\n    OptU32 = 19,\n    I64 = 20,\n    OptI64 = 21,\n    U64 = 22,\n    OptU64 = 23,\n    F32 = 24,\n    OptF32 = 25,\n    F64 = 26,\n    OptF64 = 27,\n    Str = 28,\n    OptStr = 29,\n    SharedDict = 30,\n}\n\n/// Representation of an MLT feature table layer with tag `0x01` during decoding.\n///\n/// The type parameter `S` controls how columns are stored:\n///\n/// - `Layer01<'a>` / `Layer01<'a, Lazy>` (default) — columns are [`LazyParsed`](crate::LazyParsed) enums\n///   that may be raw or decoded. Use [`Layer01::decode_all`] to transition to `Layer01<Parsed>`.\n///\n/// - `Layer01<'a, Parsed>` — all columns are fully decoded. The fields `id`, `geometry`, and\n///   `properties` hold the parsed types directly, allowing infallible readonly access.\n///   There is a `ParsedLayer01<'a>` type alias for this.\npub struct Layer01<'a, S: DecodeState = Lazy> {\n    pub name: &'a str,\n    pub extent: u32,\n    pub(crate) id: Option<Id<'a, S>>,\n    pub(crate) geometry: Geometry<'a, S>,\n    pub(crate) properties: Vec<Property<'a, S>>,\n    #[cfg(fuzzing)]\n    pub(crate) layer_order: Vec<crate::decoder::fuzzing::LayerOrdering>,\n}\n\npub type ParsedLayer01<'a> = Layer01<'a, Parsed>;\n\nimpl<'a, S> fmt::Debug for Layer01<'a, S>\nwhere\n    S: DecodeState,\n    Option<Id<'a, S>>: fmt::Debug,\n    Geometry<'a, S>: fmt::Debug,\n    Vec<Property<'a, S>>: fmt::Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let mut s = f.debug_struct(\"Layer01\");\n        s.field(\"name\", &self.name)\n            .field(\"extent\", &self.extent)\n            .field(\"id\", &self.id)\n            .field(\"geometry\", &self.geometry)\n            .field(\"properties\", &self.properties);\n        #[cfg(fuzzing)]\n        s.field(\"layer_order\", &self.layer_order);\n        s.finish()\n    }\n}\n\nimpl<'a, S> Clone for Layer01<'a, S>\nwhere\n    S: DecodeState,\n    Option<Id<'a, S>>: Clone,\n    Geometry<'a, S>: Clone,\n    Vec<Property<'a, S>>: Clone,\n{\n    fn clone(&self) -> Self {\n        Self {\n            name: self.name,\n            extent: self.extent,\n            id: self.id.clone(),\n            geometry: self.geometry.clone(),\n            properties: self.properties.clone(),\n            #[cfg(fuzzing)]\n            layer_order: self.layer_order.clone(),\n        }\n    }\n}\n\n/// Row-oriented working form for the optimizer.\n///\n/// All features are stored as a flat [`Vec<TileFeature>`] so that sorting is\n/// a single `sort_by_cached_key` call.  The `property_names` vec is parallel\n/// to every `TileFeature::properties` slice in this layer.\n#[derive(Debug, Clone, PartialEq)]\npub struct TileLayer {\n    pub name: String,\n    pub extent: u32,\n    /// Column names, parallel to `TileFeature::properties`.\n    pub property_names: Vec<String>,\n    pub features: Vec<TileFeature>,\n}\n\n/// A single map feature in row form.\n#[derive(Debug, Clone, PartialEq)]\npub struct TileFeature {\n    pub id: Option<u64>,\n    /// Geometry as a [`geo_types`] form\n    pub geometry: geo_types::Geometry<i32>,\n    /// One value per property column, in the same order as\n    /// [`TileLayer::property_names`].\n    pub properties: Vec<PropValue>,\n}\n\n/// A single typed value for one property of one feature.\n///\n/// Mirrors the scalar variants of `ParsedProperty` at the per-feature\n/// level. `SharedDict` items are flattened: each sub-field becomes its own\n/// `PropValue::Str` entry in `TileFeature::properties`, with the\n/// corresponding entry in `TileLayer::property_names` set to\n/// `\"prefix:suffix\"`.\n#[derive(Debug, Clone, PartialEq)]\npub enum PropValue {\n    Bool(Option<bool>),\n    I8(Option<i8>),\n    U8(Option<u8>),\n    I32(Option<i32>),\n    U32(Option<u32>),\n    I64(Option<i64>),\n    U64(Option<u64>),\n    F32(Option<f32>),\n    F64(Option<f64>),\n    Str(Option<String>),\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]\n#[strum(serialize_all = \"lowercase\")]\npub enum PropKind {\n    Bool,\n    I8,\n    U8,\n    I32,\n    U32,\n    I64,\n    U64,\n    F32,\n    F64,\n    Str,\n}\nimpl From<&PropValue> for PropKind {\n    fn from(prop: &PropValue) -> Self {\n        match prop {\n            PropValue::Bool(_) => Self::Bool,\n            PropValue::I8(_) => Self::I8,\n            PropValue::U8(_) => Self::U8,\n            PropValue::I32(_) => Self::I32,\n            PropValue::U32(_) => Self::U32,\n            PropValue::I64(_) => Self::I64,\n            PropValue::U64(_) => Self::U64,\n            PropValue::F32(_) => Self::F32,\n            PropValue::F64(_) => Self::F64,\n            PropValue::Str(_) => Self::Str,\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/property/decode.rs",
    "content": "use crate::decoder::{ParsedProperty, ParsedScalar, RawPresence, RawProperty};\nuse crate::utils::decode_presence;\nuse crate::{Decode, Decoder, MltResult};\n\nimpl<'a, T: Copy + PartialEq> ParsedScalar<'a, T> {\n    pub fn from_parts(\n        name: &'a str,\n        presence: RawPresence<'a>,\n        values: Vec<T>,\n        dec: &mut Decoder,\n    ) -> MltResult<Self> {\n        let presence = decode_presence(presence, values, dec)?;\n        Ok(Self { name, presence })\n    }\n}\n\nimpl<'a> Decode<ParsedProperty<'a>> for RawProperty<'a> {\n    /// Decode into a [`ParsedProperty`], charging `dec` for every heap allocation.\n    ///\n    /// For scalar columns the output size is known from stream metadata, so\n    /// the budget is charged *before* decoding.  For string and shared-dict\n    /// columns the exact decoded size depends on compression, so the budget is\n    /// charged *after* decoding based on actual allocation sizes.\n    fn decode(self, dec: &mut Decoder) -> MltResult<ParsedProperty<'a>> {\n        /// Decode the dense value stream and wrap it with the presence bitmap.\n        /// `$decode_method` is the typed `RawStream` method for element type `$ty`.\n        macro_rules! scalar_decode {\n            ($variant:ident, $ty:ty, $decode_method:ident, $v:expr, $dec:expr) => {{\n                ParsedProperty::$variant(ParsedScalar::from_parts(\n                    $v.name,\n                    $v.presence,\n                    $v.data.$decode_method($dec)?,\n                    $dec,\n                )?)\n            }};\n        }\n\n        Ok(match self {\n            Self::Bool(v) => scalar_decode!(Bool, bool, decode_bools, v, dec),\n            Self::I8(v) => scalar_decode!(I8, i8, decode_i8s, v, dec),\n            Self::U8(v) => scalar_decode!(U8, u8, decode_u8s, v, dec),\n            Self::I32(v) => scalar_decode!(I32, i32, decode_i32s, v, dec),\n            Self::U32(v) => scalar_decode!(U32, u32, decode_u32s, v, dec),\n            Self::I64(v) => scalar_decode!(I64, i64, decode_i64s, v, dec),\n            Self::U64(v) => scalar_decode!(U64, u64, decode_u64s, v, dec),\n            Self::F32(v) => scalar_decode!(F32, f32, decode_f32s, v, dec),\n            Self::F64(v) => scalar_decode!(F64, f64, decode_f64s, v, dec),\n            Self::Str(v) => ParsedProperty::Str(v.decode(dec)?),\n            Self::SharedDict(v) => ParsedProperty::SharedDict(v.decode(dec)?),\n        })\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/property/geojson.rs",
    "content": "use crate::ParsedProperty;\n\nimpl ParsedProperty<'_> {\n    #[must_use]\n    pub fn name(&self) -> &str {\n        match self {\n            Self::Bool(v) => v.name,\n            Self::I8(v) => v.name,\n            Self::U8(v) => v.name,\n            Self::I32(v) => v.name,\n            Self::U32(v) => v.name,\n            Self::I64(v) => v.name,\n            Self::U64(v) => v.name,\n            Self::F32(v) => v.name,\n            Self::F64(v) => v.name,\n            Self::Str(v) => v.name,\n            Self::SharedDict(shared_dict) => shared_dict.prefix,\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/property/mod.rs",
    "content": "mod decode;\nmod model;\npub(crate) mod strings;\n\npub use model::*;\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/property/model.rs",
    "content": "use std::borrow::Cow;\nuse std::ops::Deref;\n\nuse enum_dispatch::enum_dispatch;\n\nuse crate::decoder::RawStream;\nuse crate::utils::Presence;\nuse crate::{DecodeState, Lazy};\n\n/// Property column representation, parameterized by decode state.\n///\n/// - `Property<'a>` / `Property<'a, Lazy>` — either raw bytes or decoded, in an [`crate::LazyParsed`] enum.\n/// - `Property<'a, Parsed>` — decoded [`ParsedProperty`] directly (no enum wrapper).\npub type Property<'a, S = Lazy> =\n    <S as DecodeState>::LazyOrParsed<RawProperty<'a>, ParsedProperty<'a>>;\n\n/// Raw scalar column (bool, integer, or float) as read directly from the tile.\n#[derive(Debug, Clone, PartialEq)]\npub struct RawScalar<'a> {\n    pub(crate) name: &'a str,\n    pub(crate) presence: RawPresence<'a>,\n    pub(crate) data: RawStream<'a>,\n}\n\n/// Raw string column as read directly from the tile.\n#[derive(Debug, Clone, PartialEq)]\npub struct RawStrings<'a> {\n    pub name: &'a str,\n    pub presence: RawPresence<'a>,\n    pub encoding: RawStringsEncoding<'a>,\n}\n\n/// Raw encoding payload for a string column (plain, dictionary, or FSST variants).\n///\n/// `RawStream` order matches the encoder: see `StringEncoder.encode()`.\n#[derive(Debug, Clone, PartialEq)]\npub enum RawStringsEncoding<'a> {\n    /// Plain: length stream + data stream\n    Plain(RawPlainData<'a>),\n    /// Dictionary: lengths + offsets + dictionary data\n    Dictionary {\n        plain_data: RawPlainData<'a>,\n        offsets: RawStream<'a>,\n    },\n    /// FSST plain (4 streams): symbol lengths, symbol table, value lengths, compressed corpus. No offsets.\n    FsstPlain(RawFsstData<'a>),\n    /// FSST dictionary (5 streams): symbol lengths, symbol table, value lengths, compressed corpus, offsets.\n    FsstDictionary {\n        fsst_data: RawFsstData<'a>,\n        offsets: RawStream<'a>,\n    },\n}\n\n/// Raw encoding payload for a `SharedDict` column.\n///\n/// Unlike [`RawStringsEncoding`], shared dictionaries do NOT have their own offset stream.\n/// Instead, each child column has its own offset stream that references the shared dictionary.\n/// This is why only `Plain` and `FsstPlain` variants exist here.\n#[derive(Debug, Clone, PartialEq)]\npub enum RawSharedDictEncoding<'a> {\n    /// Plain shared dict (2 streams): lengths + data.\n    Plain(RawPlainData<'a>),\n    /// FSST plain shared dict (4 streams): symbol lengths, symbol table, lengths, corpus.\n    FsstPlain(RawFsstData<'a>),\n}\n\n/// Raw shared-dictionary column as read directly from the tile.\n#[derive(Debug, Clone, PartialEq)]\npub struct RawSharedDict<'a> {\n    pub name: &'a str,\n    pub encoding: RawSharedDictEncoding<'a>,\n    pub children: Vec<RawSharedDictItem<'a>>,\n}\n\n/// Raw property data as read directly from the tile.\n#[derive(Debug, PartialEq, Clone)]\npub enum RawProperty<'a> {\n    Bool(RawScalar<'a>),\n    I8(RawScalar<'a>),\n    U8(RawScalar<'a>),\n    I32(RawScalar<'a>),\n    U32(RawScalar<'a>),\n    I64(RawScalar<'a>),\n    U64(RawScalar<'a>),\n    F32(RawScalar<'a>),\n    F64(RawScalar<'a>),\n    Str(RawStrings<'a>),\n    SharedDict(RawSharedDict<'a>),\n}\n\n/// Parsed property values in a typed enum form.\n#[derive(Clone, Debug, PartialEq, strum::IntoStaticStr)]\n#[strum(serialize_all = \"snake_case\")]\n#[enum_dispatch(Analyze)]\npub enum ParsedProperty<'a> {\n    Bool(ParsedScalar<'a, bool>),\n    I8(ParsedScalar<'a, i8>),\n    U8(ParsedScalar<'a, u8>),\n    I32(ParsedScalar<'a, i32>),\n    U32(ParsedScalar<'a, u32>),\n    I64(ParsedScalar<'a, i64>),\n    U64(ParsedScalar<'a, u64>),\n    F32(ParsedScalar<'a, f32>),\n    F64(ParsedScalar<'a, f64>),\n    Str(ParsedStrings<'a>),\n    SharedDict(ParsedSharedDict<'a>),\n}\n\n/// Decoded scalar property column (bool, integer, or float).\n///\n/// `presence` carries both the optional bitvector and the dense values.\n/// For a non-optional column, `presence` is [`Presence::AllPresent`] with all\n/// values inline. For an optional column, `presence` is [`Presence::Bits`] with\n/// `bits.count_ones() == values.len()`.\n#[derive(Clone, Debug, PartialEq)]\npub struct ParsedScalar<'a, T: Copy + PartialEq> {\n    pub(crate) name: &'a str,\n    pub(crate) presence: Presence<'a, T>,\n}\nimpl<'a, T: Copy + PartialEq> Deref for ParsedScalar<'a, T> {\n    type Target = Presence<'a, T>;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.presence\n    }\n}\n\n/// Per-feature byte range into a shared dictionary corpus.\n///\n/// `start` and `end` are signed byte offsets into the corpus string.\n/// The sentinel value [`DictRange::NULL`] (`-1, -1`) indicates a NULL (absent) entry.\n/// Equal `start` and `end` indicate an empty string.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\npub struct DictRange {\n    pub start: i32,\n    pub end: i32,\n}\nimpl DictRange {\n    /// Sentinel value indicating a NULL (absent) entry.\n    pub const NULL: Self = Self { start: -1, end: -1 };\n}\n\n/// A single sub-property within a shared dictionary parsed value.\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub struct ParsedSharedDictItem<'a> {\n    /// The suffix name of this sub-property (appended to parent struct name).\n    pub(crate) suffix: &'a str,\n    /// Per-feature byte ranges into the parsed shared corpus.\n    /// Non-null entries indicate a present string stored as\n    /// `shared_dict.corpus()[start..end]`.\n    /// [`DictRange::NULL`] indicates a NULL value.\n    /// Equal `start` and `end` indicate an empty string.\n    pub(crate) ranges: Vec<DictRange>,\n}\n\n/// Parsed string values for a single property.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ParsedStrings<'a> {\n    pub(crate) name: &'a str,\n    /// Per-feature cumulative end offsets into `data`.\n    /// Non-negative values indicate a present string and store its exclusive\n    /// end offset in `data`.\n    /// Negative values indicate NULL and encode the current offset as `-end-1`,\n    /// which is equivalent to `!end` in two's-complement form,\n    /// so the next item can still recover its start offset without scanning back\n    /// to the previous non-null value. This allows even the first item to be NULL.\n    /// In other words, if `lengths == [5, 5, -6, 8]`, then the strings are:\n    /// ```ignore\n    /// data[0..5], // 0th string\n    /// data[5..5], // 1st string is empty\n    /// NULL,       // 2nd string, offset stays 5 because -6 == -5-1\n    /// data[5..8], // 3rd string\n    /// ```\n    pub(crate) lengths: Vec<i32>,\n    pub(crate) data: Cow<'a, str>,\n}\n\n/// Parsed shared dictionary payload shared by one or more child string properties.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ParsedSharedDict<'a> {\n    pub(crate) prefix: &'a str,\n    pub(crate) data: Cow<'a, str>,\n    pub(crate) items: Vec<ParsedSharedDictItem<'a>>,\n}\n\n/// A single child field within a `SharedDict` raw column\n#[derive(Clone, Debug, PartialEq)]\npub struct RawSharedDictItem<'a> {\n    pub name: &'a str,\n    pub presence: RawPresence<'a>,\n    pub data: RawStream<'a>,\n}\n\n/// Raw plain data (length stream + data stream) borrowed from input bytes.\n#[derive(Debug, Clone, PartialEq)]\npub struct RawPlainData<'a> {\n    pub lengths: RawStream<'a>,\n    pub data: RawStream<'a>,\n}\n\n/// Raw FSST-compressed data (4 streams) borrowed from input bytes.\n#[derive(Debug, Clone, PartialEq)]\npub struct RawFsstData<'a> {\n    pub symbol_lengths: RawStream<'a>,\n    pub symbol_table: RawStream<'a>,\n    pub lengths: RawStream<'a>,\n    pub corpus: RawStream<'a>,\n}\n\n/// Raw presence/nullability stream borrowed from input bytes.\n#[derive(Debug, Clone, PartialEq, Default)]\npub struct RawPresence<'a>(pub Option<RawStream<'a>>);\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/property/strings.rs",
    "content": "use std::borrow::Cow;\n\nuse crate::MltError::{BufferUnderflow, DictIndexOutOfBounds};\nuse crate::codecs::fsst::decode_fsst;\nuse crate::decoder::{\n    DictionaryType, LengthType, OffsetType, ParsedSharedDict, ParsedSharedDictItem, ParsedStrings,\n    RawFsstData, RawPlainData, RawPresence, RawSharedDictEncoding, RawStream, RawStrings,\n    RawStringsEncoding, StreamType,\n};\nuse crate::errors::AsMltError as _;\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, DictRange, MltError, MltResult, RawSharedDict, RawSharedDictItem};\n\nimpl<'a> ParsedStrings<'a> {\n    #[must_use]\n    pub fn new(name: &'a str, lengths: Vec<i32>, data: Cow<'a, str>) -> Self {\n        ParsedStrings {\n            name,\n            lengths,\n            data,\n        }\n    }\n\n    #[must_use]\n    pub fn feature_count(&self) -> usize {\n        self.lengths.len()\n    }\n\n    #[must_use]\n    pub fn presence_bools(&self) -> Vec<bool> {\n        self.lengths.iter().map(|&end| end >= 0).collect()\n    }\n\n    #[must_use]\n    pub fn get(&self, idx: u32) -> Option<&str> {\n        let idx = idx.as_usize();\n\n        let end = *self.lengths.get(idx)?;\n        if end < 0 {\n            return None;\n        }\n        let end = decode_end(end).as_usize();\n\n        let start = idx\n            .checked_sub(1)\n            .and_then(|prev| self.lengths.get(prev).copied())\n            .map_or(0, decode_end)\n            .as_usize();\n\n        self.data.get(start..end)\n    }\n\n    #[must_use]\n    pub fn dense_values(&self) -> Vec<String> {\n        let mut values = Vec::new();\n        let mut start = 0_u32;\n        for &end in &self.lengths {\n            let end_u32 = decode_end(end);\n            let start_idx = start.as_usize();\n            let end_idx = end_u32.as_usize();\n            if end >= 0\n                && let Some(value) = self.data.get(start_idx..end_idx)\n            {\n                values.push(value.to_string());\n            }\n            start = end_u32;\n        }\n        values\n    }\n\n    #[must_use]\n    pub fn materialize(&self) -> Vec<Option<String>> {\n        (0..u32::try_from(self.feature_count()).unwrap_or(u32::MAX))\n            .map(|i| self.get(i).map(str::to_string))\n            .collect()\n    }\n}\n\npub(crate) fn decode_shared_dict_range(range: DictRange) -> Option<(u32, u32)> {\n    if let (Ok(start), Ok(end)) = (u32::try_from(range.start), u32::try_from(range.end)) {\n        Some((start, end))\n    } else {\n        None\n    }\n}\n\npub(crate) fn shared_dict_spans(lengths: &[u32], dec: &mut Decoder) -> MltResult<Vec<(u32, u32)>> {\n    let mut spans = dec.alloc(lengths.len())?;\n    let mut offset = 0_u32;\n    for &len in lengths {\n        let start = offset;\n        offset = offset.saturating_add(len);\n        spans.push((start, offset));\n    }\n    Ok(spans)\n}\n\npub(crate) fn resolve_dict_spans(\n    offsets: &[u32],\n    presence: Option<&[bool]>,\n    dict_spans: &[(u32, u32)],\n    dec: &mut Decoder,\n) -> MltResult<Vec<Option<(u32, u32)>>> {\n    let present_count = presence.map_or(offsets.len(), <[bool]>::len);\n    let mut resolved = dec.alloc(present_count)?;\n    let mut next = offsets.iter().copied();\n\n    if let Some(presence) = presence {\n        let fail = || {\n            MltError::PresenceValueCountMismatch(\n                presence.iter().filter(|&&v| v).count(),\n                offsets.len(),\n            )\n        };\n        for &present in presence {\n            if !present {\n                resolved.push(None);\n                continue;\n            }\n            let idx = next.next().ok_or_else(fail)?;\n            let span = dict_spans\n                .get(idx as usize)\n                .copied()\n                .ok_or(DictIndexOutOfBounds(idx, dict_spans.len()))?;\n            resolved.push(Some(span));\n        }\n\n        if next.next().is_some() {\n            return Err(fail());\n        }\n    } else {\n        for &idx in offsets {\n            let span = dict_spans\n                .get(idx as usize)\n                .copied()\n                .ok_or(DictIndexOutOfBounds(idx, dict_spans.len()))?;\n            resolved.push(Some(span));\n        }\n    }\n\n    Ok(resolved)\n}\n\nfn dict_span_str(dict_data: &str, span: (u32, u32)) -> MltResult<&str> {\n    let start = span.0.as_usize();\n    let end = span.1.as_usize();\n    let bytes = dict_data.as_bytes();\n    let Some(value) = bytes.get(start..end) else {\n        let len = span.1.saturating_sub(span.0);\n        return Err(BufferUnderflow(len, bytes.len().saturating_sub(start)));\n    };\n    Ok(str::from_utf8(value)?)\n}\n\nimpl ParsedSharedDict<'_> {\n    #[must_use]\n    pub fn corpus(&self) -> &str {\n        &self.data\n    }\n\n    #[must_use]\n    pub fn get(&self, span: (u32, u32)) -> Option<&str> {\n        let start = span.0.as_usize();\n        let end = span.1.as_usize();\n        self.corpus().get(start..end)\n    }\n}\n\nimpl ParsedSharedDictItem<'_> {\n    #[must_use]\n    pub fn get<'a>(&self, shared_dict: &'a ParsedSharedDict<'_>, i: usize) -> Option<&'a str> {\n        self.ranges\n            .get(i)\n            .copied()\n            .and_then(decode_shared_dict_range)\n            .and_then(|span| shared_dict.get(span))\n    }\n}\n\nimpl<'a> RawPlainData<'a> {\n    pub fn new(lengths: RawStream<'a>, data: RawStream<'a>) -> MltResult<Self> {\n        validate_stream!(\n            lengths,\n            StreamType::Length(LengthType::VarBinary | LengthType::Dictionary)\n        );\n        validate_stream!(\n            data,\n            StreamType::Data(\n                DictionaryType::None | DictionaryType::Single | DictionaryType::Shared\n            )\n        );\n        Ok(Self { lengths, data })\n    }\n\n    pub fn decode(self, dec: &mut Decoder) -> MltResult<(&'a str, Vec<u32>)> {\n        Ok((\n            str::from_utf8(self.data.data)?,\n            self.lengths.decode_u32s(dec)?,\n        ))\n    }\n}\n\nimpl<'a> RawFsstData<'a> {\n    pub fn new(\n        symbol_lengths: RawStream<'a>,\n        symbol_table: RawStream<'a>,\n        lengths: RawStream<'a>,\n        corpus: RawStream<'a>,\n    ) -> MltResult<Self> {\n        validate_stream!(symbol_lengths, StreamType::Length(LengthType::Symbol));\n        validate_stream!(symbol_table, StreamType::Data(DictionaryType::Fsst));\n        validate_stream!(lengths, StreamType::Length(LengthType::Dictionary));\n        validate_stream!(\n            corpus,\n            StreamType::Data(DictionaryType::Single | DictionaryType::Shared)\n        );\n        Ok(Self {\n            symbol_lengths,\n            symbol_table,\n            lengths,\n            corpus,\n        })\n    }\n\n    pub fn decode(self, dec: &mut Decoder) -> MltResult<(String, Vec<u32>)> {\n        decode_fsst(self, dec)\n    }\n}\n\nimpl<'a> RawStringsEncoding<'a> {\n    #[must_use]\n    pub fn plain(plain_data: RawPlainData<'a>) -> Self {\n        Self::Plain(plain_data)\n    }\n\n    pub fn dictionary(plain_data: RawPlainData<'a>, offsets: RawStream<'a>) -> MltResult<Self> {\n        validate_stream!(offsets, StreamType::Offset(OffsetType::String));\n        Ok(Self::Dictionary {\n            plain_data,\n            offsets,\n        })\n    }\n\n    #[must_use]\n    pub fn fsst_plain(fsst_data: RawFsstData<'a>) -> Self {\n        Self::FsstPlain(fsst_data)\n    }\n\n    pub fn fsst_dictionary(fsst_data: RawFsstData<'a>, offsets: RawStream<'a>) -> MltResult<Self> {\n        validate_stream!(offsets, StreamType::Offset(OffsetType::String));\n        Ok(Self::FsstDictionary { fsst_data, offsets })\n    }\n}\n\nimpl<'a> RawSharedDictEncoding<'a> {\n    /// Plain shared dict (2 streams): lengths + data.\n    #[must_use]\n    pub fn plain(plain_data: RawPlainData<'a>) -> Self {\n        Self::Plain(plain_data)\n    }\n\n    /// FSST plain shared dict (4 streams): symbol lengths, symbol table, lengths, corpus.\n    #[must_use]\n    pub fn fsst_plain(fsst_data: RawFsstData<'a>) -> Self {\n        Self::FsstPlain(fsst_data)\n    }\n}\n\nimpl<'a> RawStrings<'a> {\n    #[must_use]\n    pub fn new(name: &'a str, presence: RawPresence<'a>, encoding: RawStringsEncoding<'a>) -> Self {\n        Self {\n            name,\n            presence,\n            encoding,\n        }\n    }\n\n    /// Decode string property from its encoded column.\n    pub fn decode(self, dec: &mut Decoder) -> MltResult<ParsedStrings<'a>> {\n        let name = self.name;\n        let presence = match self.presence.0 {\n            Some(s) => Some(s.decode_bools(dec)?),\n            None => None,\n        };\n\n        let parsed = match self.encoding {\n            RawStringsEncoding::Plain(plain_data) => {\n                let (data, lengths) = plain_data.decode(dec)?;\n                ParsedStrings {\n                    name,\n                    lengths: to_absolute_lengths(&lengths, presence.as_deref(), dec)?,\n                    data: data.into(),\n                }\n            }\n            RawStringsEncoding::Dictionary {\n                plain_data,\n                offsets,\n            } => {\n                let (data, lengths) = plain_data.decode(dec)?;\n                let offsets: Vec<u32> = offsets.decode_u32s(dec)?;\n                decode_dictionary_strings(name, &lengths, &offsets, presence.as_deref(), data, dec)?\n            }\n            RawStringsEncoding::FsstPlain(fsst_data) => {\n                let (data, dict_lens) = fsst_data.decode(dec)?;\n                ParsedStrings {\n                    name,\n                    lengths: to_absolute_lengths(&dict_lens, presence.as_deref(), dec)?,\n                    data: data.into(),\n                }\n            }\n            RawStringsEncoding::FsstDictionary { fsst_data, offsets } => {\n                let (data, lengths) = fsst_data.decode(dec)?;\n                let offsets: Vec<u32> = offsets.decode_u32s(dec)?;\n                decode_dictionary_strings(\n                    name,\n                    &lengths,\n                    &offsets,\n                    presence.as_deref(),\n                    &data,\n                    dec,\n                )?\n            }\n        };\n        Ok(parsed)\n    }\n}\n\nfn to_absolute_lengths(\n    lengths: &[u32],\n    presence: Option<&[bool]>,\n    dec: &mut Decoder,\n) -> MltResult<Vec<i32>> {\n    let capacity = presence.map_or(lengths.len(), <[bool]>::len);\n    let mut absolute = dec.alloc(capacity)?;\n    let mut iter = lengths.iter().copied();\n    let mut end = 0_i32;\n    if let Some(presence) = presence {\n        for &present in presence {\n            if present {\n                let len = iter.next().ok_or(MltError::PresenceValueCountMismatch(\n                    presence.len(),\n                    lengths.len(),\n                ))?;\n                end = checked_absolute_end(end, len)?;\n                absolute.push(end);\n            } else {\n                absolute.push(encode_null_end(end));\n            }\n        }\n        if iter.next().is_some() {\n            return Err(MltError::PresenceValueCountMismatch(\n                presence.iter().filter(|v| **v).count(),\n                lengths.len(),\n            ));\n        }\n    } else {\n        for &len in lengths {\n            end = checked_absolute_end(end, len)?;\n            absolute.push(end);\n        }\n    }\n    Ok(absolute)\n}\n\nfn decode_dictionary_strings<'a>(\n    name: &'a str,\n    dict_lengths: &[u32],\n    offsets: &[u32],\n    presence: Option<&[bool]>,\n    dict_data: &str,\n    dec: &mut Decoder,\n) -> MltResult<ParsedStrings<'a>> {\n    let dict_spans = shared_dict_spans(dict_lengths, dec)?;\n    let resolved_spans = resolve_dict_spans(offsets, presence, &dict_spans, dec)?;\n    let mut lengths = dec.alloc(resolved_spans.len())?;\n    let mut data = String::new();\n    let mut end = 0_i32;\n    for span in resolved_spans {\n        if let Some(span) = span {\n            let value = dict_span_str(dict_data, span)?;\n            data.push_str(value);\n            end = checked_string_end(end, value.len())?;\n            lengths.push(end);\n        } else {\n            lengths.push(encode_null_end(end));\n        }\n    }\n    Ok(ParsedStrings {\n        name,\n        lengths,\n        data: Cow::Owned(data),\n    })\n}\n\npub(crate) fn encode_null_end(end: i32) -> i32 {\n    -end - 1\n}\n\nfn decode_end(end: i32) -> u32 {\n    if end >= 0 {\n        u32::try_from(end).expect(\"non-negative decoded string end must fit in u32\")\n    } else {\n        u32::try_from(-i64::from(end) - 1).expect(\"encoded null boundary must fit in u32\")\n    }\n}\n\npub(crate) fn checked_string_end(current_end: i32, byte_len: usize) -> MltResult<i32> {\n    let byte_len = u32::try_from(byte_len)?;\n    checked_absolute_end(current_end, byte_len)\n}\n\npub(crate) fn checked_absolute_end(current_end: i32, delta: u32) -> MltResult<i32> {\n    let delta = i32::try_from(delta)?;\n    current_end.checked_add(delta).or_overflow()\n}\n\nimpl<'a> RawSharedDict<'a> {\n    #[must_use]\n    pub fn new(\n        name: &'a str,\n        encoding: RawSharedDictEncoding<'a>,\n        children: Vec<RawSharedDictItem<'a>>,\n    ) -> Self {\n        Self {\n            name,\n            encoding,\n            children,\n        }\n    }\n\n    /// Decode a shared-dictionary column into its decoded form.\n    pub fn decode(self, dec: &mut Decoder) -> MltResult<ParsedSharedDict<'a>> {\n        let prefix = self.name;\n        let (data, dict_spans) = match self.encoding {\n            RawSharedDictEncoding::Plain(plain_data) => {\n                let (decoded, lengths) = plain_data.decode(dec)?;\n                let dict_spans = shared_dict_spans(&lengths, dec)?;\n                (Cow::Borrowed(decoded), dict_spans)\n            }\n            RawSharedDictEncoding::FsstPlain(fsst_data) => {\n                let (decoded, lengths) = fsst_data.decode(dec)?;\n                let dict_spans = shared_dict_spans(&lengths, dec)?;\n                (decoded.into(), dict_spans)\n            }\n        };\n        let mut items = Vec::with_capacity(self.children.len());\n        for child in self.children {\n            let offsets: Vec<u32> = child.data.decode_u32s(dec)?;\n            let presence = match child.presence.0 {\n                Some(s) => Some(s.decode_bools(dec)?),\n                None => None,\n            };\n            let ranges = resolve_dict_spans(&offsets, presence.as_deref(), &dict_spans, dec)?\n                .into_iter()\n                .map(|span| match span {\n                    Some(span) => encode_shared_dict_range(span.0, span.1),\n                    None => Ok(DictRange::NULL),\n                })\n                .collect::<Result<Vec<_>, _>>()?;\n            items.push(ParsedSharedDictItem {\n                suffix: child.name,\n                ranges,\n            });\n        }\n\n        let parsed = ParsedSharedDict {\n            prefix,\n            data,\n            items,\n        };\n        // Corpus size is only known after decompression; charge after.\n        let bytes = parsed.items.iter().try_fold(\n            u32::try_from(parsed.data.len()).or_overflow()?,\n            |acc, item| {\n                let n = u32::try_from(item.ranges.len() * size_of::<DictRange>()).or_overflow()?;\n                acc.checked_add(n).or_overflow()\n            },\n        )?;\n        dec.consume(bytes)?;\n        Ok(parsed)\n    }\n}\n\npub(crate) fn encode_shared_dict_range(start: u32, end: u32) -> MltResult<DictRange> {\n    Ok(DictRange {\n        start: i32::try_from(start)?,\n        end: i32::try_from(end)?,\n    })\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/root.rs",
    "content": "use crate::LazyParsed::Raw;\nuse crate::MltError::{\n    BufferUnderflow, GeometryWithoutStreams, InvalidSharedDictStreamCount, MissingGeometry,\n    MultipleGeometryColumns, MultipleIdColumns, SharedDictRequiresStreams, TrailingLayerData,\n    UnexpectedStructChildCount, UnsupportedStringStreamCount,\n};\nuse crate::codecs::varint::parse_varint;\nuse crate::decoder::{\n    Column, ColumnType, DictionaryType, Geometry, Id, Layer01, ParsedLayer01, RawFsstData,\n    RawGeometry, RawId, RawIdValue, RawPlainData, RawPresence, RawProperty, RawScalar,\n    RawSharedDict, RawSharedDictEncoding, RawSharedDictItem, RawStream, RawStrings,\n    RawStringsEncoding, StreamType,\n};\nuse crate::errors::AsMltError as _;\nuse crate::utils::{AsUsize as _, SetOptionOnce as _, parse_string};\nuse crate::{Layer, Lazy, MltError, MltRefResult, MltResult, ParsedLayer};\n\n/// Default memory budget: 20 MiB.\nconst DEFAULT_MAX_BYTES: u32 = 20 * 1024 * 1024;\n\n/// Stateful decoder that enforces a per-tile memory budget during decoding.\n///\n/// Pass a `Decoder` to every `raw.decode()` / `into_tile()` call and to\n/// `from_bytes`-style parsers. Each method charges the budget before\n/// performing heap allocations, so the total heap used never exceeds `max_bytes`\n/// (in bytes).\n///\n/// ```\n/// use mlt_core::Decoder;\n///\n/// // Default: 10 MiB budget.\n/// let mut dec = Decoder::default();\n///\n/// // Custom budget.\n/// let mut dec = Decoder::with_max_size(64 * 1024 * 1024);\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq, Default)]\npub struct Decoder {\n    /// Keep track of the memory used when decoding a tile: raw->parsed transition\n    budget: MemBudget,\n    /// Reusable scratch buffer for the physical u32 decode pass.\n    /// Held here so its heap allocation is reused across streams without extra cost.\n    pub(crate) buffer_u32: Vec<u32>,\n    /// Reusable scratch buffer for the physical u64 decode pass.\n    /// Held here so its heap allocation is reused across streams without extra cost.\n    pub(crate) buffer_u64: Vec<u64>,\n}\n\nimpl Decoder {\n    /// Create a decoder with a custom memory budget (in bytes).\n    #[must_use]\n    pub fn with_max_size(max_bytes: u32) -> Self {\n        Self {\n            budget: MemBudget::with_max_size(max_bytes),\n            ..Default::default()\n        }\n    }\n\n    pub fn decode_all<'a>(\n        &mut self,\n        layers: impl IntoIterator<Item = Layer<'a>>,\n    ) -> MltResult<Vec<ParsedLayer<'a>>> {\n        layers\n            .into_iter()\n            .map(|l| l.decode_all(self))\n            .collect::<MltResult<_>>()\n    }\n\n    /// Allocate a `Vec<T>` with the given capacity, charging the decoder's budget for\n    /// `capacity * size_of::<T>()` bytes. Use this instead of `Vec::with_capacity` in decode paths.\n    #[inline]\n    pub(crate) fn alloc<T>(&mut self, capacity: usize) -> MltResult<Vec<T>> {\n        let bytes = capacity.checked_mul(size_of::<T>()).or_overflow()?;\n        let bytes_u32 = u32::try_from(bytes).or_overflow()?;\n        self.budget.consume(bytes_u32)?;\n        Ok(Vec::with_capacity(capacity))\n    }\n\n    /// Charge the budget for `size` raw bytes. Prefer [`consume_items`][Self::consume_items]\n    /// when charging for a known-type collection.\n    #[inline]\n    pub(crate) fn consume(&mut self, size: u32) -> MltResult<()> {\n        self.budget.consume(size)\n    }\n\n    /// Charge the budget for `count` items of type `T` (`count * size_of::<T>()` bytes).\n    #[inline]\n    pub(crate) fn consume_items<T>(&mut self, count: usize) -> MltResult<()> {\n        let bytes = count.checked_mul(size_of::<T>()).or_overflow()?;\n        self.budget.consume(u32::try_from(bytes).or_overflow()?)\n    }\n\n    #[inline]\n    pub(crate) fn adjust(&mut self, adjustment: u32) {\n        self.budget.adjust(adjustment);\n    }\n\n    /// Return the unused portion of a pre-charged allocation budget.\n    ///\n    /// Call this after fully populating a `Vec<T>` that was pre-allocated with [`Decoder::alloc`],\n    /// passing the same `alloc_size` that was given to `alloc`.\n    ///\n    /// Returns an error if the vector grew beyond `alloc_size` (malformed input caused more items\n    /// than declared). Subtracts `(alloc_size - buf.len()) * size_of::<T>()` from the budget.\n    #[inline]\n    pub(crate) fn adjust_alloc<T>(&mut self, buf: &[T], alloc_size: usize) -> MltResult<()> {\n        if buf.len() > alloc_size {\n            return Err(MltError::InvalidDecodingStreamSize(buf.len(), alloc_size));\n        }\n        // Return the unused portion of the pre-charged budget.\n        let unused = (alloc_size - buf.len()) * size_of::<T>();\n        // unused fits in u32: it's at most alloc_size * size_of::<T>(), which was checked to fit\n        // in u32 when alloc() was called. Using saturating_cast to avoid a fallible conversion.\n        #[expect(\n            clippy::cast_possible_truncation,\n            reason = \"unused <= alloc_size * size_of::<T>() which was verified to fit in u32 by alloc()\"\n        )]\n        self.budget.adjust(unused as u32);\n        Ok(())\n    }\n\n    #[must_use]\n    pub fn consumed(&self) -> u32 {\n        self.budget.consumed()\n    }\n\n    /// Reset the memory budget to zero, keeping scratch buffers allocated.\n    ///\n    /// Call this between tiles when reusing a single `Decoder` for multiple\n    /// decodes — the per-tile budget is enforced fresh, but the internal\n    /// `buffer_u32` / `buffer_u64` scratch space is retained so it doesn't\n    /// need to be re-allocated.\n    ///\n    /// # Safety / correctness precondition\n    ///\n    /// Only call this after dropping any decoded allocations returned from the\n    /// previous tile. Resetting the budget while earlier decoded outputs are\n    /// still alive makes the budget enforceable only per-tile and can bypass\n    /// the stronger guarantee that total live heap tracked by this decoder\n    /// never exceeds the configured maximum.\n    pub fn reset_budget(&mut self) {\n        self.budget.reset();\n    }\n}\n\nimpl MemBudget {\n    /// Reset tracked usage for a new decode window.\n    ///\n    /// Callers must ensure that allocations accounted for by the previous\n    /// window are no longer live before resetting.\n    fn reset(&mut self) {\n        self.bytes_used = 0;\n    }\n}\n/// Stateful parser that enforces a memory budget during parsing (binary → raw structures).\n///\n/// The parse chain reserves memory before allocations so total heap stays within the limit.\n///\n/// ```\n/// use mlt_core::Parser;\n///\n/// # let bytes: &[u8] = &[];\n/// let mut parser = Parser::default();\n/// let layers = parser.parse_layers(bytes).expect(\"parse\");\n///\n/// // Or with a custom limit:\n/// let mut parser = Parser::with_max_size(64 * 1024 * 1024);\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq, Default)]\npub struct Parser {\n    budget: MemBudget,\n}\n\nimpl Parser {\n    /// Create a parser with a custom memory budget (in bytes).\n    #[must_use]\n    pub fn with_max_size(max_bytes: u32) -> Self {\n        Self {\n            budget: MemBudget::with_max_size(max_bytes),\n        }\n    }\n\n    /// Parse a sequence of binary layers, reserving decoded memory against this parser's budget.\n    pub fn parse_layers<'a>(&mut self, mut input: &'a [u8]) -> MltResult<Vec<Layer<'a>>> {\n        let mut result = Vec::new();\n        while !input.is_empty() {\n            let layer;\n            (input, layer) = Layer::from_bytes(input, self)?;\n            result.push(layer);\n        }\n        Ok(result)\n    }\n\n    /// Reserve `size` bytes from the parse budget. Used internally by the parse chain.\n    #[inline]\n    pub(crate) fn reserve(&mut self, size: u32) -> MltResult<()> {\n        self.budget.consume(size)\n    }\n\n    #[must_use]\n    pub fn reserved(&self) -> u32 {\n        self.budget.consumed()\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\nstruct MemBudget {\n    /// Hard ceiling: total decoded bytes may not exceed this value.\n    pub max_bytes: u32,\n    /// Running total of used bytes so far.\n    pub bytes_used: u32,\n}\n\nimpl Default for MemBudget {\n    /// Create a decoder with the default 10 MiB memory budget.\n    fn default() -> Self {\n        Self::with_max_size(DEFAULT_MAX_BYTES)\n    }\n}\n\nimpl MemBudget {\n    /// Create a decoder with a custom memory budget (in bytes).\n    #[must_use]\n    fn with_max_size(max_bytes: u32) -> Self {\n        Self {\n            max_bytes,\n            bytes_used: 0,\n        }\n    }\n\n    /// Adjust previous consumption by `- adjustment` bytes.  Will panic if used incorrectly.\n    #[inline]\n    fn adjust(&mut self, adjustment: u32) {\n        self.bytes_used = self.bytes_used.checked_sub(adjustment).unwrap();\n    }\n\n    /// Take `size` bytes from the allocation budget. Call this before the actual allocation.\n    #[inline]\n    fn consume(&mut self, size: u32) -> MltResult<()> {\n        let accumulator = &mut self.bytes_used;\n        let max_bytes = self.max_bytes;\n        if let Some(new_value) = accumulator\n            .checked_add(size)\n            .and_then(|v| if v > max_bytes { None } else { Some(v) })\n        {\n            *accumulator = new_value;\n            Ok(())\n        } else {\n            Err(MltError::MemoryLimitExceeded {\n                limit: max_bytes,\n                used: *accumulator,\n                requested: size,\n            })\n        }\n    }\n\n    fn consumed(&self) -> u32 {\n        self.bytes_used\n    }\n}\n\nimpl<'a> Layer01<'a, Lazy> {\n    /// Parse `v01::Layer` metadata, reserving decoded memory against the parser's budget.\n    pub(crate) fn from_bytes(input: &'a [u8], parser: &mut Parser) -> MltResult<Self> {\n        let (input, layer_name) = parse_string(input)?;\n        let (input, extent) = parse_varint::<u32>(input)?;\n        let (input, column_count) = parse_varint::<u32>(input)?;\n\n        // Each column requires at least 1 byte (column type)\n        if input.len() < column_count.as_usize() {\n            return Err(BufferUnderflow(column_count, input.len()));\n        }\n\n        // !!!!!!!\n        // WARNING: make sure to never use `let (input, ...)` after this point: input var is reused\n        let (mut input, (col_info, prop_count)) = parse_columns_meta(input, column_count, parser)?;\n        #[cfg(fuzzing)]\n        let layer_order = col_info\n            .iter()\n            .map(|column| column.typ)\n            .map(crate::decoder::fuzzing::LayerOrdering::from)\n            .collect();\n\n        let mut properties = Vec::with_capacity(prop_count.as_usize());\n        let mut id_column: Option<Id> = None;\n        let mut geometry: Option<Geometry> = None;\n\n        for column in col_info {\n            use crate::decoder::RawProperty as RP;\n\n            let opt;\n            let value;\n            let name = column.name.unwrap_or(\"\");\n\n            match column.typ {\n                ColumnType::Id | ColumnType::OptId => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    id_column.set_once(Raw(RawId {\n                        presence: RawPresence(opt),\n                        value: RawIdValue::Id32(value),\n                    }))?;\n                }\n                ColumnType::LongId | ColumnType::OptLongId => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    id_column.set_once(Raw(RawId {\n                        presence: RawPresence(opt),\n                        value: RawIdValue::Id64(value),\n                    }))?;\n                }\n                ColumnType::Geometry => {\n                    input = parse_geometry_column(input, &mut geometry, parser)?;\n                }\n                ColumnType::Bool | ColumnType::OptBool => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::parse_bool(input, parser)?;\n                    properties.push(Raw(RP::Bool(scalar(name, opt, value))));\n                }\n                ColumnType::I8 | ColumnType::OptI8 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::I8(scalar(name, opt, value))));\n                }\n                ColumnType::U8 | ColumnType::OptU8 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::U8(scalar(name, opt, value))));\n                }\n                ColumnType::I32 | ColumnType::OptI32 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::I32(scalar(name, opt, value))));\n                }\n                ColumnType::U32 | ColumnType::OptU32 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::U32(scalar(name, opt, value))));\n                }\n                ColumnType::I64 | ColumnType::OptI64 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::I64(scalar(name, opt, value))));\n                }\n                ColumnType::U64 | ColumnType::OptU64 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::U64(scalar(name, opt, value))));\n                }\n                ColumnType::F32 | ColumnType::OptF32 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::F32(scalar(name, opt, value))));\n                }\n                ColumnType::F64 | ColumnType::OptF64 => {\n                    (input, opt) = parse_optional(column.typ, input, parser)?;\n                    (input, value) = RawStream::from_bytes(input, parser)?;\n                    properties.push(Raw(RP::F64(scalar(name, opt, value))));\n                }\n                ColumnType::Str | ColumnType::OptStr => {\n                    let prop;\n                    (input, prop) = parse_str_column(input, name, column.typ, parser)?;\n                    properties.push(Raw(prop));\n                }\n                ColumnType::SharedDict => {\n                    let prop;\n                    (input, prop) = parse_shared_dict_column(input, &column, parser)?;\n                    properties.push(Raw(prop));\n                }\n            }\n        }\n        if input.is_empty() {\n            Ok(Layer01 {\n                name: layer_name,\n                extent,\n                id: id_column,\n                geometry: geometry.ok_or(MissingGeometry)?,\n                properties,\n                #[cfg(fuzzing)]\n                layer_order,\n            })\n        } else {\n            Err(TrailingLayerData(input.len()))\n        }\n    }\n\n    /// Decode all columns and transition to [`Layer01<Parsed>`].\n    ///\n    /// Consumes `self` (a `Layer01<Lazy>`) and returns a `Layer01<Parsed>` where every\n    /// column field holds its parsed value directly, enabling infallible readonly access.\n    pub fn decode_all(self, dec: &mut Decoder) -> MltResult<ParsedLayer01<'a>> {\n        Ok(Layer01 {\n            name: self.name,\n            extent: self.extent,\n            id: self.id.map(|id| id.into_parsed(dec)).transpose()?,\n            geometry: self.geometry.into_parsed(dec)?,\n            properties: self\n                .properties\n                .into_iter()\n                .map(|p| p.into_parsed(dec))\n                .collect::<MltResult<Vec<_>>>()?,\n            #[cfg(fuzzing)]\n            layer_order: self.layer_order,\n        })\n    }\n}\n\nfn parse_struct_children<'a>(\n    mut input: &'a [u8],\n    column: &Column<'a>,\n    parser: &mut Parser,\n) -> MltRefResult<'a, Vec<RawSharedDictItem<'a>>> {\n    let mut children = Vec::with_capacity(column.children.len());\n    for child in &column.children {\n        let (inp, sc) = parse_varint::<u32>(input)?;\n        let (inp, child_optional) = parse_optional(child.typ, inp, parser)?;\n        let optional_stream_count = u32::from(child_optional.is_some());\n        if let Some(data_count) = sc.checked_sub(optional_stream_count)\n            && data_count != 1\n        {\n            return Err(UnexpectedStructChildCount(data_count));\n        }\n        let (inp, child_data) = RawStream::from_bytes(inp, parser)?;\n        children.push(RawSharedDictItem {\n            name: child.name.unwrap_or(\"\"),\n            presence: RawPresence(child_optional),\n            data: child_data,\n        });\n        input = inp;\n    }\n    Ok((input, children))\n}\n\nfn parse_optional<'a>(\n    typ: ColumnType,\n    input: &'a [u8],\n    parser: &mut Parser,\n) -> MltRefResult<'a, Option<RawStream<'a>>> {\n    if typ.is_optional() {\n        let (input, optional) = RawStream::parse_bool(input, parser)?;\n        Ok((input, Some(optional)))\n    } else {\n        Ok((input, None))\n    }\n}\n\nfn parse_geometry_column<'a>(\n    input: &'a [u8],\n    geometry: &mut Option<Geometry<'a>>,\n    parser: &mut Parser,\n) -> MltResult<&'a [u8]> {\n    let (input, stream_count) = parse_varint::<u32>(input)?;\n    if stream_count == 0 {\n        return Err(GeometryWithoutStreams);\n    }\n    // Each stream requires at least 1 byte (physical stream type)\n    let stream_count_capa = stream_count.as_usize();\n    if input.len() < stream_count_capa {\n        return Err(BufferUnderflow(stream_count, input.len()));\n    }\n    // metadata\n    let (input, meta) = RawStream::from_bytes(input, parser)?;\n    // geometry items\n    let (input, items) = RawStream::parse_multiple(input, stream_count_capa - 1, parser)?;\n    geometry.set_once(Raw(RawGeometry { meta, items }))?;\n    Ok(input)\n}\n\nfn parse_str_column<'a>(\n    mut input: &'a [u8],\n    name: &'a str,\n    typ: ColumnType,\n    parser: &mut Parser,\n) -> MltRefResult<'a, RawProperty<'a>> {\n    let mut stream_count = {\n        let stream_count_u32;\n        (input, stream_count_u32) = parse_varint::<u32>(input)?;\n        stream_count_u32.as_usize()\n    };\n    let presence;\n    (input, presence) = parse_optional(typ, input, parser)?;\n    if presence.is_some() {\n        if stream_count == 0 {\n            return Err(UnsupportedStringStreamCount(stream_count));\n        }\n        stream_count -= 1;\n    }\n    let mut str_streams = [None, None, None, None, None];\n    if stream_count > str_streams.len() {\n        return Err(UnsupportedStringStreamCount(stream_count));\n    }\n    for slot in str_streams.iter_mut().take(stream_count) {\n        let stream;\n        (input, stream) = RawStream::from_bytes(input, parser)?;\n        *slot = Some(stream);\n    }\n    let encoding = match str_streams {\n        [Some(s1), Some(s2), None, None, None] => {\n            RawStringsEncoding::plain(RawPlainData::new(s1, s2)?)\n        }\n        [Some(s1), Some(s2), Some(s3), None, None] => {\n            RawStringsEncoding::dictionary(RawPlainData::new(s1, s3)?, s2)?\n        }\n        [Some(s1), Some(s2), Some(s3), Some(s4), None] => {\n            RawStringsEncoding::fsst_plain(RawFsstData::new(s1, s2, s3, s4)?)\n        }\n        [Some(s1), Some(s2), Some(s3), Some(s4), Some(s5)] => {\n            RawStringsEncoding::fsst_dictionary(RawFsstData::new(s1, s2, s3, s4)?, s5)?\n        }\n        _ => Err(UnsupportedStringStreamCount(stream_count))?,\n    };\n    Ok((\n        input,\n        RawProperty::Str(RawStrings {\n            name,\n            presence: RawPresence(presence),\n            encoding,\n        }),\n    ))\n}\n\nfn parse_shared_dict_column<'a>(\n    mut input: &'a [u8],\n    column: &Column<'a>,\n    parser: &mut Parser,\n) -> MltRefResult<'a, RawProperty<'a>> {\n    // Read header streams until we hit the dictionary DATA(Single|Shared) stream.\n    let stream_count;\n    (input, stream_count) = parse_varint::<u32>(input)?;\n    let mut dict_streams = [None, None, None, None, None];\n    let mut streams_taken = 0_usize;\n    while streams_taken < stream_count.as_usize() {\n        let stream;\n        (input, stream) = RawStream::from_bytes(input, parser)?;\n        let is_last = matches!(\n            stream.meta.stream_type,\n            StreamType::Data(DictionaryType::Single | DictionaryType::Shared)\n        );\n        dict_streams[streams_taken] = Some(stream);\n        streams_taken += 1;\n        if is_last {\n            break;\n        } else if streams_taken >= dict_streams.len() {\n            return Err(UnsupportedStringStreamCount(streams_taken + 1));\n        }\n    }\n    let children;\n    (input, children) = parse_struct_children(input, column, parser)?;\n\n    // Validate stream_count: must equal dict_streams + children + optional_children.\n    let children_n = u32::try_from(children.len()).or_overflow()?;\n    let optional_n = children\n        .iter()\n        .filter(|c| c.presence.0.is_some())\n        .count()\n        .try_into()\n        .or_overflow()?;\n    let dict_n = u32::try_from(streams_taken).or_overflow()?;\n    let expected = crate::utils::checked_sum3(dict_n, children_n, optional_n)?;\n    // Java's encoder had a bug (fixed) that overcounted by 1: dict + 2*N + 1.\n    // Accept that value too so that files produced by older Java encoders still parse.\n    let java_legacy = expected.checked_add(1).or_overflow()?;\n    if stream_count != expected && stream_count != java_legacy {\n        return Err(InvalidSharedDictStreamCount {\n            actual: stream_count,\n            expected,\n        });\n    }\n\n    let name = column.name.unwrap_or(\"\");\n    let encoding = match dict_streams {\n        [Some(s1), Some(s2), None, None, None] => {\n            RawSharedDictEncoding::plain(RawPlainData::new(s1, s2)?)\n        }\n        [Some(s1), Some(s2), Some(s3), Some(s4), None] => {\n            RawSharedDictEncoding::fsst_plain(RawFsstData::new(s1, s2, s3, s4)?)\n        }\n        _ => Err(SharedDictRequiresStreams(streams_taken))?,\n    };\n    Ok((\n        input,\n        RawProperty::SharedDict(RawSharedDict {\n            name,\n            encoding,\n            children,\n        }),\n    ))\n}\n\nfn parse_columns_meta<'a>(\n    mut input: &'a [u8],\n    column_count: u32,\n    parser: &mut Parser,\n) -> MltRefResult<'a, (Vec<Column<'a>>, u32)> {\n    use crate::decoder::ColumnType::{Geometry, Id, LongId, OptId, OptLongId, SharedDict};\n\n    let mut col_info = Vec::with_capacity(column_count.as_usize());\n    let mut geometries = 0;\n    let mut ids = 0;\n    for _ in 0..column_count {\n        let mut typ;\n        (input, typ) = Column::from_bytes(input, parser)?;\n        match typ.typ {\n            Geometry => geometries += 1,\n            Id | OptId | LongId | OptLongId => ids += 1,\n            SharedDict => {\n                // Yes, we need to parse children right here; otherwise this messes up the next column\n                let child_column_count;\n                (input, child_column_count) = parse_varint::<u32>(input)?;\n\n                // Each column requires at least 1 byte (ColumnType without a name)\n                let child_col_capacity = child_column_count.as_usize();\n                if input.len() < child_col_capacity {\n                    return Err(BufferUnderflow(child_column_count, input.len()));\n                }\n                let mut children = Vec::with_capacity(child_col_capacity);\n                for _ in 0..child_column_count {\n                    let child;\n                    (input, child) = Column::from_bytes(input, parser)?;\n                    children.push(child);\n                }\n                typ.children = children;\n            }\n            _ => {}\n        }\n        col_info.push(typ);\n    }\n    if geometries > 1 {\n        return Err(MultipleGeometryColumns);\n    }\n    if ids > 1 {\n        return Err(MultipleIdColumns);\n    }\n\n    Ok((input, (col_info, column_count - geometries - ids)))\n}\n\nfn scalar<'a>(name: &'a str, opt: Option<RawStream<'a>>, value: RawStream<'a>) -> RawScalar<'a> {\n    RawScalar {\n        name,\n        presence: RawPresence(opt),\n        data: value,\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/analyze.rs",
    "content": "use crate::decoder::{RawStream, StreamMeta};\nuse crate::{Analyze, StatType};\n\nimpl Analyze for RawStream<'_> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        self.data.collect_statistic(stat)\n    }\n\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        cb(self.meta);\n    }\n}\n\nimpl Analyze for StreamMeta {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        if stat == StatType::DecodedMetaSize {\n            size_of::<Self>()\n        } else {\n            0\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/decode.rs",
    "content": "use std::borrow::Cow;\nuse std::mem;\n\nuse bitvec::prelude::{BitSlice, BitVec, Lsb0};\nuse bitvec::view::BitView as _;\n\nuse crate::codecs::bytes::{decode_bytes_to_bools, decode_bytes_to_u32s, decode_bytes_to_u64s};\nuse crate::codecs::fastpfor::decode_fastpfor;\nuse crate::codecs::rle::decode_byte_rle;\nuse crate::codecs::varint::parse_varint_vec;\nuse crate::decoder::{LogicalEncoding, LogicalValue, PhysicalEncoding, RawStream};\nuse crate::errors::{AsMltError as _, fail_if_invalid_stream_size};\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltError, MltResult};\n\nimpl<'a> RawStream<'a> {\n    /// Decode a presence/nullability stream into a packed bitvector.\n    ///\n    /// Borrows directly from tile bytes (zero-copy) when both logical and physical\n    /// encodings are `None`; otherwise decompresses byte-RLE into an owned `BitVec`.\n    /// The result is always truncated to exactly `num_values` bits.\n    pub(crate) fn decode_bitvec(self, dec: &mut Decoder) -> MltResult<Cow<'a, BitSlice<u8, Lsb0>>> {\n        let num_values = self.meta.num_values.as_usize();\n        if self.meta.encoding.physical == PhysicalEncoding::VarInt {\n            return Err(MltError::NotImplemented(\"varint presence decoding\"));\n        }\n        if self.meta.encoding.logical == LogicalEncoding::None\n            && self.meta.encoding.physical == PhysicalEncoding::None\n        {\n            // Zero-copy: raw tile bytes are the packed bitvector.\n            let num_bytes = num_values.div_ceil(8);\n            fail_if_invalid_stream_size(self.data.len(), num_bytes)?;\n            Ok(Cow::Borrowed(&self.data.view_bits::<Lsb0>()[..num_values]))\n        } else {\n            let num_bytes = num_values.div_ceil(8);\n            let bytes = decode_byte_rle(self.data, num_bytes, dec)?;\n            let mut bvec = BitVec::<u8, Lsb0>::from_vec(bytes);\n            bvec.truncate(num_values);\n            Ok(Cow::Owned(bvec))\n        }\n    }\n\n    /// Decode a boolean data stream: byte-RLE → packed bitmap → `Vec<bool>`, charging `dec`.\n    pub fn decode_bools(self, dec: &mut Decoder) -> MltResult<Vec<bool>> {\n        if self.meta.encoding.physical == PhysicalEncoding::VarInt {\n            return Err(MltError::NotImplemented(\"varint bool decoding\"));\n        }\n        let num_values = self.meta.num_values.as_usize();\n        let num_bytes = num_values.div_ceil(8);\n        let decoded = decode_byte_rle(self.data, num_bytes, dec)?;\n        decode_bytes_to_bools(&decoded, num_values, dec)\n    }\n\n    pub fn decode_i8s(self, dec: &mut Decoder) -> MltResult<Vec<i8>> {\n        self.decode_i32s(dec)?\n            .into_iter()\n            .map(i8::try_from)\n            .collect::<Result<Vec<_>, _>>()\n            .map_err(Into::into)\n    }\n\n    pub fn decode_u8s(self, dec: &mut Decoder) -> MltResult<Vec<u8>> {\n        self.decode_u32s(dec)?\n            .into_iter()\n            .map(u8::try_from)\n            .collect::<Result<Vec<u8>, _>>()\n            .map_err(Into::into)\n    }\n\n    pub fn decode_i32s(self, dec: &mut Decoder) -> MltResult<Vec<i32>> {\n        let meta = self.meta;\n        // i32 always needs a logical transform (zigzag at minimum) — use scratch buffer.\n        let mut buf = mem::take(&mut dec.buffer_u32);\n        self.decode_bits_u32(&mut buf, dec)?;\n        let result = LogicalValue::new(meta).decode_i32(&buf, dec);\n        dec.buffer_u32 = buf;\n        dec.buffer_u32.clear();\n        result\n    }\n\n    pub fn decode_u32s(self, dec: &mut Decoder) -> MltResult<Vec<u32>> {\n        let meta = self.meta;\n        if meta.encoding.logical == LogicalEncoding::None {\n            // No logical transform: physical words are the output — decode into a fresh Vec.\n            let mut out = Vec::new();\n            self.decode_bits_u32(&mut out, dec)?;\n            Ok(out)\n        } else {\n            // Logical transform needed — use the reusable scratch buffer.\n            let mut buf = mem::take(&mut dec.buffer_u32);\n            self.decode_bits_u32(&mut buf, dec)?;\n            let result = LogicalValue::new(meta).decode_u32(&buf, dec);\n            dec.buffer_u32 = buf;\n            dec.buffer_u32.clear();\n            result\n        }\n    }\n\n    pub fn decode_u64s(self, dec: &mut Decoder) -> MltResult<Vec<u64>> {\n        let meta = self.meta;\n        if meta.encoding.logical == LogicalEncoding::None {\n            // No logical transform: physical words are the output — decode into a fresh Vec.\n            let mut out = Vec::new();\n            self.decode_bits_u64(&mut out, dec)?;\n            Ok(out)\n        } else {\n            // Logical transform needed — use the reusable scratch buffer.\n            let mut buf = mem::take(&mut dec.buffer_u64);\n            self.decode_bits_u64(&mut buf, dec)?;\n            let result = LogicalValue::new(meta).decode_u64(&buf, dec);\n            dec.buffer_u64 = buf;\n            dec.buffer_u64.clear();\n            result\n        }\n    }\n\n    pub fn decode_i64s(self, dec: &mut Decoder) -> MltResult<Vec<i64>> {\n        let meta = self.meta;\n        // i64 always needs a logical transform (zigzag at minimum) — use scratch buffer.\n        let mut buf = mem::take(&mut dec.buffer_u64);\n        self.decode_bits_u64(&mut buf, dec)?;\n        let result = LogicalValue::new(meta).decode_i64(&buf, dec);\n        dec.buffer_u64 = buf;\n        dec.buffer_u64.clear();\n        result\n    }\n\n    /// Decode a stream of f32 values from raw little-endian bytes, charging `dec`.\n    pub fn decode_f32s(self, dec: &mut Decoder) -> MltResult<Vec<f32>> {\n        if self.meta.encoding.physical == PhysicalEncoding::VarInt {\n            return Err(MltError::NotImplemented(\"varint f32 decoding\"));\n        }\n        let num = self.meta.num_values.as_usize();\n        dec.consume_items::<f32>(num)?;\n        fail_if_invalid_stream_size(self.data.len(), num.checked_mul(4).or_overflow()?)?;\n\n        Ok(self\n            .data\n            .chunks_exact(4)\n            .map(|chunk| f32::from_le_bytes(chunk.try_into().expect(\"infallible: chunks_exact(4)\")))\n            .collect())\n    }\n\n    /// Decode a stream of f64 values from raw little-endian bytes, charging `dec`.\n    pub fn decode_f64s(self, dec: &mut Decoder) -> MltResult<Vec<f64>> {\n        if self.meta.encoding.physical == PhysicalEncoding::VarInt {\n            return Err(MltError::NotImplemented(\"varint f64 decoding\"));\n        }\n        let num = self.meta.num_values.as_usize();\n        fail_if_invalid_stream_size(self.data.len(), num.checked_mul(8).or_overflow()?)?;\n\n        dec.consume_items::<f64>(num)?;\n        Ok(self\n            .data\n            .chunks_exact(8)\n            .map(|chunk| f64::from_le_bytes(chunk.try_into().expect(\"infallible: chunks_exact(8)\")))\n            .collect())\n    }\n\n    /// Physically decode the stream into `buf` as `u32` values.\n    ///\n    /// `buf` is cleared and filled with the decoded words. The caller owns the\n    /// buffer and is responsible for deciding whether it constitutes a final\n    /// persistent allocation (and therefore should be charged to a [`Decoder`]).\n    pub fn decode_bits_u32(self, buf: &mut Vec<u32>, dec: &mut Decoder) -> MltResult<()> {\n        buf.clear();\n        match self.meta.encoding.physical {\n            PhysicalEncoding::None => {\n                let (_, values) = decode_bytes_to_u32s(self.data, self.meta.num_values, dec)?;\n                *buf = values;\n            }\n            PhysicalEncoding::FastPFor256 => {\n                *buf = decode_fastpfor(self.data, self.meta.num_values, dec)?;\n            }\n            PhysicalEncoding::VarInt => {\n                let (_, values) =\n                    parse_varint_vec::<u32, u32>(self.data, self.meta.num_values, dec)?;\n                *buf = values;\n            }\n        }\n        Ok(())\n    }\n\n    /// Physically decode the stream into `buf` as `u64` values.\n    ///\n    /// `buf` is cleared and filled with the decoded words. The caller owns the\n    /// buffer and is responsible for deciding whether it constitutes a final\n    /// persistent allocation (and therefore should be charged to a [`Decoder`]).\n    pub fn decode_bits_u64(self, buf: &mut Vec<u64>, dec: &mut Decoder) -> MltResult<()> {\n        buf.clear();\n        match self.meta.encoding.physical {\n            PhysicalEncoding::None => {\n                let (_, values) = decode_bytes_to_u64s(self.data, self.meta.num_values, dec)?;\n                *buf = values;\n            }\n            PhysicalEncoding::FastPFor256 => {\n                return Err(MltError::UnsupportedPhysicalEncoding(\n                    \"FastPFOR decoding u64\",\n                ));\n            }\n            PhysicalEncoding::VarInt => {\n                let (_, values) =\n                    parse_varint_vec::<u64, u64>(self.data, self.meta.num_values, dec)?;\n                *buf = values;\n            }\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/logical.rs",
    "content": "use std::fmt::Debug;\nuse std::iter::repeat_n;\n\nuse num_traits::{PrimInt, ToPrimitive as _};\n\nuse crate::MltError::{ParsingLogicalTechnique, RleRunLenInvalid, UnsupportedLogicalEncoding};\nuse crate::codecs::zigzag::{decode_componentwise_delta_vec2s, decode_zigzag, decode_zigzag_delta};\nuse crate::decoder::{LogicalEncoding, LogicalTechnique, LogicalValue, RleMeta, StreamMeta};\nuse crate::errors::{AsMltError as _, fail_if_invalid_stream_size};\nuse crate::utils::AsUsize as _;\nuse crate::{Decoder, MltResult};\n\nimpl RleMeta {\n    /// Decode RLE (Run-Length Encoding) data.\n    /// Charges the decoder for the expanded output allocation.\n    pub fn decode<T: PrimInt + Debug>(self, data: &[T], dec: &mut Decoder) -> MltResult<Vec<T>> {\n        let expected_len = self.runs.as_usize().checked_mul(2).or_overflow()?;\n        fail_if_invalid_stream_size(data.len(), expected_len)?;\n\n        let (run_lens, values) = data.split_at(self.runs.as_usize());\n        fail_if_invalid_stream_size(self.num_rle_values, Self::calc_size(run_lens)?)?;\n\n        let alloc_size = self.num_rle_values.as_usize();\n        let mut result = dec.alloc(alloc_size)?;\n        for (&run_len, &val) in run_lens.iter().zip(values.iter()) {\n            let run = run_len\n                .to_usize()\n                .ok_or_else(|| RleRunLenInvalid(run_len.to_i128().unwrap_or_default()))?;\n            result.extend(repeat_n(val, run));\n        }\n        dec.adjust_alloc(&result, alloc_size)?;\n        Ok(result)\n    }\n\n    fn calc_size<T: PrimInt + Debug>(run_lens: &[T]) -> MltResult<u32> {\n        run_lens\n            .iter()\n            .try_fold(T::zero(), |a, v| a.checked_add(v))\n            .and_then(|v| v.to_u32())\n            .ok_or_else(|| RleRunLenInvalid(run_lens.len().to_i128().unwrap_or_default()))\n    }\n}\n\nimpl LogicalTechnique {\n    pub fn parse(value: u8) -> MltResult<Self> {\n        Self::try_from(value).or(Err(ParsingLogicalTechnique(value)))\n    }\n}\n\nimpl LogicalValue {\n    #[must_use]\n    pub fn new(meta: StreamMeta) -> Self {\n        Self { meta }\n    }\n\n    /// Logically decode `data` (physically decoded u32 words) into `Vec<i32>`.\n    ///\n    /// Never called for `LogicalEncoding::None` — that case is handled directly\n    /// in the bridge (physical buffer decoded into a fresh output Vec).\n    pub fn decode_i32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {\n        match self.meta.encoding.logical {\n            LogicalEncoding::None => decode_zigzag(data, dec),\n            LogicalEncoding::Rle(v) => decode_zigzag(&v.decode(data, dec)?, dec),\n            LogicalEncoding::ComponentwiseDelta => decode_componentwise_delta_vec2s(data, dec),\n            LogicalEncoding::Delta => decode_zigzag_delta::<i32, _>(data, dec),\n            LogicalEncoding::DeltaRle(v) => {\n                let expanded = v.decode(data, dec)?;\n                decode_zigzag_delta::<i32, _>(&expanded, dec)\n            }\n            LogicalEncoding::Morton(v) => v.decode_codes(data, dec),\n            LogicalEncoding::MortonDelta(v) => v.decode_delta(data, dec),\n            LogicalEncoding::MortonRle(_) => Err(UnsupportedLogicalEncoding(\n                self.meta.encoding.logical,\n                \"i32 (MortonRle)\",\n            )),\n            LogicalEncoding::PseudoDecimal => Err(UnsupportedLogicalEncoding(\n                self.meta.encoding.logical,\n                \"i32\",\n            )),\n        }\n    }\n\n    /// Logically decode `data` (physically decoded u32 words) into `Vec<u32>`.\n    ///\n    /// Not called for `LogicalEncoding::None` — that case is handled entirely\n    /// in the bridge (physical buffer decoded directly into the output Vec).\n    pub fn decode_u32(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<u32>> {\n        let num = self.meta.num_values.as_usize();\n        match self.meta.encoding.logical {\n            LogicalEncoding::None => {\n                // Caller should have used the direct-output path; this is a fallback.\n                dec.consume_items::<u32>(num)?;\n                Ok(data.to_vec())\n            }\n            LogicalEncoding::Rle(rle) => rle.decode(data, dec),\n            LogicalEncoding::Delta => decode_zigzag_delta::<i32, _>(data, dec),\n            LogicalEncoding::DeltaRle(rle) => {\n                decode_zigzag_delta::<i32, _>(&rle.decode(data, dec)?, dec)\n            }\n            _ => Err(UnsupportedLogicalEncoding(\n                self.meta.encoding.logical,\n                \"u32\",\n            )),\n        }\n    }\n\n    /// Logically decode `data` (physically decoded u64 words) into `Vec<i64>`.\n    ///\n    /// Never called for `LogicalEncoding::None` — that case is handled directly\n    /// in the bridge (physical buffer decoded into a fresh output Vec).\n    pub fn decode_i64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<i64>> {\n        match self.meta.encoding.logical {\n            LogicalEncoding::None => decode_zigzag(data, dec),\n            LogicalEncoding::Delta => decode_zigzag_delta::<i64, _>(data, dec),\n            LogicalEncoding::DeltaRle(rle) => {\n                let expanded = rle.decode(data, dec)?;\n                decode_zigzag_delta::<i64, _>(&expanded, dec)\n            }\n            LogicalEncoding::Rle(rle) => {\n                // rle.decode() charges for expanded u64 vec; decode_zigzag charges for i64 vec\n                let expanded = rle.decode(data, dec)?;\n                decode_zigzag(&expanded, dec)\n            }\n            _ => Err(UnsupportedLogicalEncoding(\n                self.meta.encoding.logical,\n                \"i64\",\n            )),\n        }\n    }\n\n    /// Logically decode `data` (physically decoded u64 words) into `Vec<u64>`.\n    ///\n    /// Not called for `LogicalEncoding::None` — that case is handled entirely\n    /// in the bridge (physical buffer decoded directly into the output Vec).\n    pub fn decode_u64(self, data: &[u64], dec: &mut Decoder) -> MltResult<Vec<u64>> {\n        let num = self.meta.num_values.as_usize();\n        match self.meta.encoding.logical {\n            LogicalEncoding::None => {\n                // Caller should have used the direct-output path; this is a fallback.\n                dec.consume_items::<u64>(num)?;\n                Ok(data.to_vec())\n            }\n            LogicalEncoding::Rle(rle) => rle.decode(data, dec),\n            LogicalEncoding::Delta => decode_zigzag_delta::<i64, _>(data, dec),\n            LogicalEncoding::DeltaRle(rle) => {\n                let expanded = rle.decode(data, dec)?;\n                decode_zigzag_delta::<i64, _>(&expanded, dec)\n            }\n            _ => Err(UnsupportedLogicalEncoding(\n                self.meta.encoding.logical,\n                \"u64\",\n            )),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::MltError::InvalidDecodingStreamSize;\n    use crate::test_helpers::dec;\n\n    #[test]\n    fn test_decode_rle_empty() {\n        let rle = RleMeta {\n            runs: 0,\n            num_rle_values: 0,\n        };\n        assert!(rle.decode::<u32>(&[], &mut dec()).unwrap().is_empty());\n    }\n\n    #[test]\n    fn test_decode_rle_invalid_stream_size() {\n        // Valid RLE for runs=2 needs 4 elements (2 run lengths + 2 values). Only 3 provided.\n        let rle = RleMeta {\n            runs: 2,\n            num_rle_values: 3,\n        };\n        let data = [1u32, 2, 3];\n        let err = rle.decode::<u32>(&data, &mut dec()).unwrap_err();\n        assert!(matches!(err, InvalidDecodingStreamSize(3, 4)));\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/mod.rs",
    "content": "mod analyze;\nmod decode;\npub(crate) mod logical;\npub(crate) mod model;\nmod parse;\nmod physical;\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/model.rs",
    "content": "use derive_debug::Dbg;\nuse num_enum::TryFromPrimitive;\n\nuse crate::utils::formatter::{bytes_dbg, compact_dbg};\nuse crate::{MltError, MltResult};\n\n/// Logical encoding technique used for a column, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, TryFromPrimitive)]\n#[repr(u8)]\npub enum LogicalTechnique {\n    None = 0,\n    Delta = 1,\n    ComponentwiseDelta = 2,\n    Rle = 3,\n    Morton = 4,\n    PseudoDecimal = 5,\n}\n\n/// Metadata for RLE decoding\n/// TODO v2 optimizations:\n///   * runs is identical to half the size of the associated array\n///   * `num_rle_values` is identical to the size of the sum of the first half of the array.\n///     Computing checked sum should not be too expensive.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct RleMeta {\n    pub(crate) runs: u32,\n    pub(crate) num_rle_values: u32,\n}\n\n/// Metadata for Morton decoding\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Morton {\n    /// Number of bits used\n    pub(crate) bits: u32,\n    /// Coordinate shift\n    pub(crate) shift: u32,\n}\n\nimpl Morton {\n    pub fn new(bits: u32, shift: u32) -> MltResult<Self> {\n        if bits <= 16 {\n            Ok(Self { bits, shift })\n        } else {\n            Err(MltError::InvalidMortonBits(bits))\n        }\n    }\n}\n\n/// How should the stream be interpreted at the logical level (second pass of decoding)\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum LogicalEncoding {\n    None,\n    Delta,\n    DeltaRle(RleMeta),\n    ComponentwiseDelta,\n    Rle(RleMeta),\n    Morton(Morton),\n    MortonDelta(Morton),\n    MortonRle(Morton),\n    PseudoDecimal,\n}\n\n/// Carries the stream metadata needed to perform the logical decode pass.\n///\n/// Construct with [`LogicalValue::new`] after the physical decode pass fills a\n/// `&[u32]` or `&[u64]` buffer, then call the appropriate `decode_*` method,\n/// passing that slice as `data`.\n#[derive(Debug, PartialEq)]\npub struct LogicalValue {\n    pub(crate) meta: StreamMeta,\n}\n\n// Physical encoding types\n\n/// Dictionary type used for a column, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]\n#[repr(u8)]\npub enum DictionaryType {\n    None = 0,\n    Single = 1,\n    Shared = 2,\n    Vertex = 3,\n    Morton = 4,\n    Fsst = 5,\n}\n\n/// Offset type used for a column, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]\n#[repr(u8)]\npub enum OffsetType {\n    Vertex = 0,\n    Index = 1,\n    String = 2,\n    Key = 3,\n}\n\n/// Length type used for a column, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]\n#[repr(u8)]\npub enum LengthType {\n    VarBinary = 0,\n    Geometries = 1,\n    Parts = 2,\n    Rings = 3,\n    Triangles = 4,\n    Symbol = 5,\n    Dictionary = 6,\n}\n\n/// How should the stream be interpreted at the physical level (first pass of decoding)\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]\npub enum StreamType {\n    Present,\n    Data(DictionaryType),\n    Offset(OffsetType),\n    Length(LengthType),\n}\n\n/// Physical encoding used for a column, as stored in the tile\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, TryFromPrimitive)]\n#[repr(u8)]\npub enum PhysicalEncoding {\n    None = 0,\n    /// Preferred, tends to produce the best compression ratio and decoding performance.\n    /// But currently limited to 32-bit integer.\n    FastPFor256 = 1,\n    /// Can produce better results in combination with a heavyweight compression scheme like `Gzip`.\n    /// Simple compression scheme where the encoding is easier to implement compared to `FastPfor`.\n    VarInt = 2,\n}\n\n// RawStream types\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct IntEncoding {\n    pub logical: LogicalEncoding,\n    pub physical: PhysicalEncoding,\n}\n\n/// Metadata about an encoded stream\n#[derive(Clone, Copy, Dbg, PartialEq)]\npub struct StreamMeta {\n    #[dbg(formatter = \"compact_dbg\")]\n    pub stream_type: StreamType,\n    #[dbg(formatter = \"compact_dbg\")]\n    pub encoding: IntEncoding,\n    pub(crate) num_values: u32,\n}\n\n/// Representation of an encoded stream\n#[derive(Clone, Dbg, PartialEq)]\npub struct RawStream<'a> {\n    pub meta: StreamMeta,\n    #[dbg(formatter = \"bytes_dbg\")]\n    pub(crate) data: &'a [u8],\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/parse.rs",
    "content": "use std::io;\n\nuse integer_encoding::VarIntWriter as _;\n\nuse crate::codecs::varint::parse_varint;\nuse crate::decoder::{\n    IntEncoding, LogicalEncoding, LogicalTechnique, Morton, PhysicalEncoding, RawStream, RleMeta,\n    StreamMeta, StreamType,\n};\nuse crate::errors::{AsMltError as _, fail_if_invalid_stream_size};\nuse crate::utils::{AsUsize as _, BinarySerializer as _, parse_u8, take};\nuse crate::{MltError, MltRefResult, MltResult, Parser};\n\nimpl IntEncoding {\n    #[must_use]\n    pub(crate) const fn new(logical: LogicalEncoding, physical: PhysicalEncoding) -> Self {\n        Self { logical, physical }\n    }\n\n    #[must_use]\n    pub(crate) const fn none() -> Self {\n        Self::new(LogicalEncoding::None, PhysicalEncoding::None)\n    }\n}\n\nimpl StreamMeta {\n    #[inline]\n    pub(crate) fn new(stream_type: StreamType, encoding: IntEncoding, num_values: u32) -> Self {\n        Self {\n            stream_type,\n            encoding,\n            num_values,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn new2(\n        stream_type: StreamType,\n        logical: LogicalEncoding,\n        physical: PhysicalEncoding,\n        num_values: usize,\n    ) -> MltResult<Self> {\n        let enc = IntEncoding::new(logical, physical);\n        Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))\n    }\n\n    #[inline]\n    pub(crate) fn new_none(stream_type: StreamType, num_values: usize) -> MltResult<Self> {\n        let enc = IntEncoding::none();\n        Ok(Self::new(stream_type, enc, u32::try_from(num_values)?))\n    }\n\n    /// Parse stream from the input\n    ///\n    /// If `is_bool` is true, compute RLE parameters for boolean streams\n    /// automatically instead of reading them from the input.\n    ///\n    /// Returns the stream metadata and the size of the stream in bytes.\n    /// Reserves an upper-bound estimate of decoded bytes (`num_values * 8`) on the parser\n    /// for all stream types. RLE uses `num_rle_values * 8` since that is the actual expanded count.\n    pub(crate) fn from_bytes<'a>(\n        input: &'a [u8],\n        is_bool: bool,\n        parser: &mut Parser,\n    ) -> MltRefResult<'a, (Self, u32)> {\n        use crate::decoder::LogicalTechnique as LT;\n\n        let (input, stream_type) = StreamType::from_bytes(input)?;\n        let (input, val) = parse_u8(input)?;\n        let logical1 = LT::parse(val >> 5)?;\n        let logical2 = LT::parse((val >> 2) & 0x7)?;\n        let physical_encoding = PhysicalEncoding::parse(val & 0x3)?;\n\n        let (input, num_values) = parse_varint::<u32>(input)?;\n        let (input, byte_length) = parse_varint::<u32>(input)?;\n\n        let mut input = input;\n        let logical_encoding = match (logical1, logical2) {\n            (LT::None | LT::Delta | LT::ComponentwiseDelta | LT::PseudoDecimal, LT::None) => {\n                // Reserve decoded memory upper bound: worst case u64 = 8 bytes per value\n                let decoded_bytes = num_values.saturating_mul(8);\n                parser.reserve(decoded_bytes)?;\n                match logical1 {\n                    LT::None => LogicalEncoding::None,\n                    LT::Delta => LogicalEncoding::Delta,\n                    LT::ComponentwiseDelta => LogicalEncoding::ComponentwiseDelta,\n                    _ => LogicalEncoding::PseudoDecimal,\n                }\n            }\n            (LT::Delta, LT::Rle) | (LT::Rle, LT::None) => {\n                let runs;\n                let num_rle_values;\n                if is_bool {\n                    runs = num_values.div_ceil(8);\n                    num_rle_values = byte_length;\n                } else {\n                    (input, runs) = parse_varint::<u32>(input)?;\n                    (input, num_rle_values) = parse_varint::<u32>(input)?;\n                }\n                // Reserve decoded memory (worst case: u64 = 8 bytes per value)\n                let decoded_bytes = num_rle_values.saturating_mul(8);\n                parser.reserve(decoded_bytes)?;\n                let rle = RleMeta {\n                    runs,\n                    num_rle_values,\n                };\n                if logical1 == LT::Rle {\n                    LogicalEncoding::Rle(rle)\n                } else {\n                    LogicalEncoding::DeltaRle(rle)\n                }\n            }\n            (LT::Morton, LT::None | LT::Rle | LT::Delta) => {\n                // Reserve decoded memory upper bound: worst case u64 = 8 bytes per value\n                let decoded_bytes = num_values.saturating_mul(8);\n                parser.reserve(decoded_bytes)?;\n                let bits;\n                let shift;\n                (input, bits) = parse_varint::<u32>(input)?;\n                (input, shift) = parse_varint::<u32>(input)?;\n                let morton = Morton::new(bits, shift)?;\n                match logical2 {\n                    LT::Rle => LogicalEncoding::MortonRle(morton),\n                    LT::Delta => LogicalEncoding::MortonDelta(morton),\n                    _ => LogicalEncoding::Morton(morton),\n                }\n            }\n            _ => Err(MltError::InvalidLogicalEncodings(logical1, logical2))?,\n        };\n\n        let meta = Self::new(\n            stream_type,\n            IntEncoding::new(logical_encoding, physical_encoding),\n            num_values,\n        );\n        Ok((input, (meta, byte_length)))\n    }\n\n    pub(crate) fn write_to<W: io::Write>(\n        &self,\n        writer: &mut W,\n        is_bool: bool,\n        byte_length: u32,\n    ) -> io::Result<()> {\n        use LogicalEncoding as LE;\n        use LogicalTechnique as LT;\n\n        writer.write_u8(self.stream_type.as_u8())?;\n        let logical_enc_u8: u8 = match self.encoding.logical {\n            LE::None => (LT::None as u8) << 5,\n            LE::Delta => (LT::Delta as u8) << 5,\n            LE::DeltaRle(_) => ((LT::Delta as u8) << 5) | ((LT::Rle as u8) << 2),\n            LE::ComponentwiseDelta => (LT::ComponentwiseDelta as u8) << 5,\n            LE::Rle(_) => (LT::Rle as u8) << 5,\n            LE::Morton(_) => (LT::Morton as u8) << 5,\n            LE::MortonRle(_) => (LT::Morton as u8) << 5 | ((LT::Rle as u8) << 2),\n            LE::MortonDelta(_) => (LT::Morton as u8) << 5 | ((LT::Delta as u8) << 2),\n            LE::PseudoDecimal => (LT::PseudoDecimal as u8) << 5,\n        };\n        let physical_enc_u8: u8 = match self.encoding.physical {\n            PhysicalEncoding::None => 0x0,\n            PhysicalEncoding::FastPFor256 => 0x1,\n            PhysicalEncoding::VarInt => 0x2,\n        };\n        writer.write_u8(logical_enc_u8 | physical_enc_u8)?;\n        writer.write_varint(self.num_values)?;\n        writer.write_varint(byte_length)?;\n\n        // some encoding have settings inside them\n        match self.encoding.logical {\n            LE::DeltaRle(r) | LE::Rle(r) => {\n                if !is_bool {\n                    writer.write_varint(r.runs)?;\n                    writer.write_varint(r.num_rle_values)?;\n                }\n            }\n            LE::Morton(m) | LE::MortonDelta(m) | LE::MortonRle(m) => {\n                writer.write_varint(m.bits)?;\n                writer.write_varint(m.shift)?;\n            }\n            LE::None | LE::Delta | LE::ComponentwiseDelta | LE::PseudoDecimal => {}\n        }\n        Ok(())\n    }\n}\n\nimpl<'a> RawStream<'a> {\n    #[must_use]\n    pub(crate) fn new(meta: StreamMeta, data: &'a [u8]) -> Self {\n        Self { meta, data }\n    }\n\n    pub(crate) fn from_bytes(input: &'a [u8], parser: &mut Parser) -> MltRefResult<'a, Self> {\n        Self::from_bytes_internal(input, false, parser)\n    }\n\n    pub(crate) fn parse_multiple(\n        mut input: &'a [u8],\n        count: usize,\n        parser: &mut Parser,\n    ) -> MltRefResult<'a, Vec<Self>> {\n        let mut result = Vec::with_capacity(count);\n        for _ in 0..count {\n            let stream;\n            (input, stream) = RawStream::from_bytes_internal(input, false, parser)?;\n            result.push(stream);\n        }\n        Ok((input, result))\n    }\n\n    pub(crate) fn parse_bool(input: &'a [u8], parser: &mut Parser) -> MltRefResult<'a, Self> {\n        Self::from_bytes_internal(input, true, parser)\n    }\n\n    /// Parse stream from the input\n    /// If `is_bool` is true, compute RLE parameters for boolean streams\n    /// automatically instead of reading them from the input.\n    /// For RLE streams with `VarInt` data, validates that run lengths sum to `num_rle_values`.\n    fn from_bytes_internal(\n        input: &'a [u8],\n        is_bool: bool,\n        parser: &mut Parser,\n    ) -> MltRefResult<'a, Self> {\n        use LogicalEncoding as LE;\n        use PhysicalEncoding as PD;\n\n        let (input, (meta, byte_length)) = StreamMeta::from_bytes(input, is_bool, parser)?;\n        let (input, data) = take(input, byte_length)?;\n\n        // For RLE with VarInt physical encoding, validate stream: run lengths must sum to num_rle_values\n        if let LE::Rle(r) | LE::DeltaRle(r) = meta.encoding.logical\n            && matches!(meta.encoding.physical, PD::VarInt)\n            && !is_bool\n        {\n            validate_rle_varint_stream(data, r.runs, r.num_rle_values)?;\n        }\n\n        Ok((input, RawStream::new(meta, data)))\n    }\n}\n\n/// Validate RLE stream data: first `runs` varints must sum to `num_rle_values`.\nfn validate_rle_varint_stream(data: &[u8], runs: u32, num_rle_values: u32) -> MltResult<()> {\n    let mut rest = data;\n    let mut sum: u64 = 0;\n    for _ in 0..runs {\n        let (next, len) = parse_varint::<u32>(rest)?;\n        rest = next;\n        sum = sum.checked_add(len.into()).or_overflow()?;\n    }\n    if sum != u64::from(num_rle_values) {\n        let sum_usize = usize::try_from(sum).map_err(|_| MltError::IntegerOverflow)?;\n        fail_if_invalid_stream_size(sum_usize, num_rle_values.as_usize())?;\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/stream/physical.rs",
    "content": "use crate::MltError::ParsingStreamType;\nuse crate::MltRefResult;\nuse crate::decoder::{DictionaryType, LengthType, OffsetType, StreamType};\nuse crate::utils::parse_u8;\n\nimpl StreamType {\n    pub fn from_bytes(input: &'_ [u8]) -> MltRefResult<'_, Self> {\n        let (input, value) = parse_u8(input)?;\n        let pt = Self::from_u8(value).ok_or(ParsingStreamType(value))?;\n        Ok((input, pt))\n    }\n\n    fn from_u8(value: u8) -> Option<Self> {\n        let high4 = value >> 4;\n        let low4 = value & 0x0F;\n        Some(match high4 {\n            #[cfg(fuzzing)]\n            // when fuzzing, we cannot have ignored bits, to preserve roundtrip-ability\n            0 if low4 == 0 => StreamType::Present,\n            #[cfg(not(fuzzing))]\n            0 => Self::Present,\n            1 => Self::Data(DictionaryType::try_from(low4).ok()?),\n            2 => Self::Offset(OffsetType::try_from(low4).ok()?),\n            3 => Self::Length(LengthType::try_from(low4).ok()?),\n            _ => return None,\n        })\n    }\n    #[must_use]\n    pub fn as_u8(self) -> u8 {\n        let proto_high4 = match self {\n            Self::Present => 0,\n            Self::Data(_) => 1,\n            Self::Offset(_) => 2,\n            Self::Length(_) => 3,\n        };\n        let high4 = proto_high4 << 4;\n        let low4 = match self {\n            Self::Present => 0,\n            Self::Data(i) => i as u8,\n            Self::Offset(i) => i as u8,\n            Self::Length(i) => i as u8,\n        };\n        debug_assert!(low4 <= 0x0F, \"secondary types should not exceed 4 bit\");\n        high4 | low4\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/decoder/tile.rs",
    "content": "//! Row-oriented \"source form\" for the optimizer.\n//!\n//! [`TileLayer`] holds one [`TileFeature`] per map feature, each owning\n//! its geometry as a [`geo_types::Geometry<i32>`] and its property values as a\n//! plain `Vec<PropValue>`.  This is the working form used throughout the\n//! optimizer and sorting pipeline: it is cheap to clone, trivially sortable,\n//! and free from any encoded/decoded duality.\n\nuse crate::decoder::{\n    GeometryValues, Layer01, ParsedLayer01, ParsedProperty, PropValue, PropValueRef, TileFeature,\n    TileLayer,\n};\nuse crate::errors::AsMltError as _;\nuse crate::{Decoder, LendingIterator, MltResult};\n\nimpl ParsedLayer01<'_> {\n    /// Returns the decoded geometry buffer for this layer.\n    ///\n    /// Provides access to the columnar geometry arrays (vertex buffer, offset arrays, geometry\n    /// types) for advanced use cases such as building typed arrays for WebAssembly or\n    /// performing spatial indexing. For iterating feature geometries as `geo_types` values,\n    /// prefer [`iter_features`](Self::iter_features) instead.\n    #[must_use]\n    pub fn geometry_values(&self) -> &GeometryValues {\n        &self.geometry\n    }\n\n    /// Decode and convert into a row-oriented [`TileLayer`], charging every\n    /// heap allocation against `dec`.\n    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {\n        // Extract owned/copied fields before borrowing self for the feature iterator.\n        let name = self.name.to_string();\n        let extent = self.extent;\n        let names: Vec<String> = self.iterate_prop_names().map(|n| n.to_string()).collect();\n        let col_nulls = typed_nulls(&self.properties);\n        let mut features = dec.alloc::<TileFeature>(self.feature_count())?;\n        let mut feat_iter = self.iter_features();\n        while let Some(feat) = feat_iter.next() {\n            let feat = feat?;\n            let mut values = dec.alloc::<PropValue>(names.len())?;\n            for (col_idx, value) in feat.iter_all_properties().enumerate() {\n                values.push(match value {\n                    Some(v) => prop_value_from_ref(v),\n                    None => col_nulls[col_idx].clone(),\n                });\n            }\n\n            charge_str_props(dec, &values)?;\n\n            features.push(TileFeature {\n                id: feat.id,\n                geometry: feat.geometry,\n                properties: values,\n            });\n        }\n\n        Ok(TileLayer {\n            name,\n            extent,\n            property_names: names,\n            features,\n        })\n    }\n\n    #[must_use]\n    pub fn feature_count(&self) -> usize {\n        self.geometry.vector_types.len()\n    }\n}\n\nimpl Layer01<'_> {\n    /// Decode and convert into a row-oriented [`TileLayer`]\n    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {\n        self.decode_all(dec)?.into_tile(dec)\n    }\n}\n\n/// Convert a [`PropValueRef`] (as yielded by [`crate::FeatureRef::iter_all_properties`])\n/// into an owned [`PropValue`].\nfn prop_value_from_ref(value: PropValueRef<'_>) -> PropValue {\n    match value {\n        PropValueRef::Bool(v) => PropValue::Bool(Some(v)),\n        PropValueRef::I8(v) => PropValue::I8(Some(v)),\n        PropValueRef::U8(v) => PropValue::U8(Some(v)),\n        PropValueRef::I32(v) => PropValue::I32(Some(v)),\n        PropValueRef::U32(v) => PropValue::U32(Some(v)),\n        PropValueRef::I64(v) => PropValue::I64(Some(v)),\n        PropValueRef::U64(v) => PropValue::U64(Some(v)),\n        PropValueRef::F32(v) => PropValue::F32(Some(v)),\n        PropValueRef::F64(v) => PropValue::F64(Some(v)),\n        PropValueRef::Str(s) => PropValue::Str(Some(s.to_string())),\n    }\n}\n\n/// Build a flat list of typed null [`PropValue`]s, one per logical column position\n/// as yielded by [`crate::FeatureRef::iter_all_properties`].\n///\n/// Each scalar column contributes one entry with its specific null variant (e.g.\n/// `PropValue::Bool(None)`).  A `SharedDict` column expands to one `PropValue::Str(None)`\n/// entry per sub-item.\nfn typed_nulls(properties: &[ParsedProperty<'_>]) -> Vec<PropValue> {\n    use ParsedProperty as PP;\n    use PropValue as PV;\n    let mut nulls = Vec::new();\n    for prop in properties {\n        match prop {\n            PP::Bool(_) => nulls.push(PV::Bool(None)),\n            PP::I8(_) => nulls.push(PV::I8(None)),\n            PP::U8(_) => nulls.push(PV::U8(None)),\n            PP::I32(_) => nulls.push(PV::I32(None)),\n            PP::U32(_) => nulls.push(PV::U32(None)),\n            PP::I64(_) => nulls.push(PV::I64(None)),\n            PP::U64(_) => nulls.push(PV::U64(None)),\n            PP::F32(_) => nulls.push(PV::F32(None)),\n            PP::F64(_) => nulls.push(PV::F64(None)),\n            PP::Str(_) => nulls.push(PV::Str(None)),\n            PP::SharedDict(d) => {\n                for _ in &d.items {\n                    nulls.push(PV::Str(None));\n                }\n            }\n        }\n    }\n    nulls\n}\n\n/// Charge `dec` for the heap bytes of owned `String` values inside `PropValue::Str`.\nfn charge_str_props(dec: &mut Decoder, props: &[PropValue]) -> MltResult<()> {\n    let str_bytes = props\n        .iter()\n        .filter_map(|p| {\n            if let PropValue::Str(Some(s)) = p {\n                Some(s.len())\n            } else {\n                None\n            }\n        })\n        .try_fold(0u32, |acc, n| {\n            acc.checked_add(u32::try_from(n).or_overflow()?)\n                .or_overflow()\n        })?;\n    if str_bytes > 0 {\n        dec.consume(str_bytes)?;\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/analyze.rs",
    "content": "#[cfg(any(test, feature = \"__private\"))]\nuse crate::decoder::StreamMeta;\nuse crate::decoder::{ParsedScalar, ParsedSharedDict, ParsedStrings};\n#[cfg(any(test, feature = \"__private\"))]\nuse crate::encoder::EncodedStream;\nuse crate::{Analyze, StatType};\n\n#[cfg(any(test, feature = \"__private\"))]\nimpl Analyze for EncodedStream {\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        cb(self.meta);\n    }\n}\n\nimpl<T: Analyze + Copy + PartialEq> Analyze for ParsedScalar<'_, T> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        let meta = if stat == StatType::DecodedMetaSize {\n            self.name.len()\n        } else {\n            0\n        };\n        meta + self.presence.collect_statistic(stat)\n    }\n}\n\nimpl Analyze for ParsedSharedDict<'_> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        let meta = if stat == StatType::DecodedMetaSize {\n            self.prefix.len() + self.items.iter().map(|v| v.suffix.len()).sum::<usize>()\n        } else {\n            0\n        };\n        meta + self.data.len()\n    }\n}\n\nimpl Analyze for ParsedStrings<'_> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        let meta = if stat == StatType::DecodedMetaSize {\n            self.name.len()\n        } else {\n            0\n        };\n        meta + self.dense_values().collect_statistic(stat)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/fuzzing.rs",
    "content": "use arbitrary::Error::IncorrectFormat;\nuse arbitrary::{Arbitrary, Result, Unstructured};\n\nuse crate::encoder::model::StagedLayer;\nuse crate::encoder::optimizer::Presence;\nuse crate::encoder::{StagedId, StagedProperty, StagedSharedDict, StagedStrings};\n\nimpl Arbitrary<'_> for StagedId {\n    fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {\n        // Bound ID count to prevent OOM from unbounded vector generation\n        let count = u.int_in_range(0..=64u8)? as usize;\n        let values: Vec<Option<u64>> = (0..count).map(|_| u.arbitrary()).collect::<Result<_>>()?;\n        Ok(Self::from_optional(values))\n    }\n}\n\nimpl Arbitrary<'_> for StagedLayer {\n    fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {\n        // Bound name length to prevent OOM from unbounded string generation\n        let name_len = u.int_in_range(0..=32u8)? as usize;\n        let name: String = (0..name_len)\n            .map(|_| u.arbitrary::<char>())\n            .collect::<Result<_>>()?;\n        let extent: u32 = u.arbitrary()?;\n        // Generate geometry first -- its feature count drives ID and property columns.\n        let geometry: crate::decoder::GeometryValues = u.arbitrary()?;\n        let fc = geometry.vector_types().len();\n        let id = if u.arbitrary::<bool>()? {\n            let ids: Vec<Option<u64>> = (0..fc)\n                .map(|_| -> Result<_> {\n                    if u.arbitrary::<bool>()? {\n                        Ok(Some(u.arbitrary::<u64>()?))\n                    } else {\n                        Ok(None)\n                    }\n                })\n                .collect::<Result<_>>()?;\n            StagedId::from_optional(ids)\n        } else {\n            StagedId::None\n        };\n        // Bound property count to prevent OOM from unbounded vector generation.\n        // Each column must have exactly `fc` values to match the feature count.\n        let prop_count = u.int_in_range(0..=4u8)? as usize;\n        let properties: Vec<StagedProperty> = (0..prop_count)\n            .map(|_| {\n                let values: Vec<Option<u32>> =\n                    (0..fc).map(|_| u.arbitrary()).collect::<Result<_>>()?;\n                Ok(StagedProperty::opt_u32(\"prop\", values))\n            })\n            .collect::<Result<_>>()?;\n\n        Ok(Self {\n            name,\n            extent,\n            id,\n            geometry,\n            properties,\n        })\n    }\n}\n\nimpl<'a> Arbitrary<'a> for StagedSharedDict {\n    fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {\n        // Bound item count and string sizes to prevent OOM\n        let item_count = u.int_in_range(0..=8u8)? as usize;\n        let items_raw: Vec<(String, Vec<Option<String>>, Presence)> = (0..item_count)\n            .map(|_| {\n                let values = generate_strings(u)?;\n                let presence = if values.iter().all(Option::is_some) {\n                    Presence::AllPresent\n                } else {\n                    Presence::Mixed\n                };\n                Ok((bounded_string(u, 32)?, values, presence))\n            })\n            .collect::<Result<_>>()?;\n        if items_raw.is_empty() {\n            return Ok(Self {\n                prefix: bounded_string(u, 32)?,\n                data: String::new(),\n                items: Vec::new(),\n            });\n        }\n        let prefix = bounded_string(u, 32)?;\n        Self::new(prefix, items_raw).map_err(|_| IncorrectFormat)\n    }\n}\n\nimpl Arbitrary<'_> for StagedProperty {\n    fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {\n        // Bound value count to prevent OOM from unbounded vector generation\n        let count = u.int_in_range(0..=64u8)? as usize;\n        let values: Vec<Option<u32>> = (0..count).map(|_| u.arbitrary()).collect::<Result<_>>()?;\n        Ok(Self::opt_u32(\"prop\", values))\n    }\n}\n\nimpl Arbitrary<'_> for StagedStrings {\n    fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> {\n        Ok(Self::from_optional(\n            bounded_string(u, 32)?,\n            generate_strings(u)?,\n        ))\n    }\n}\n\n/// Generate a string with bounded length to prevent OOM from unbounded string generation.\npub fn bounded_string(u: &mut Unstructured<'_>, max_len: u8) -> Result<String> {\n    let len = u.int_in_range(0..=max_len)? as usize;\n    (0..len)\n        .map(|_| u.arbitrary::<char>())\n        .collect::<Result<_>>()\n}\n\nfn generate_strings(u: &mut Unstructured) -> Result<Vec<Option<String>>> {\n    // Bound string count and individual string lengths to prevent OOM\n    let val_count = u.int_in_range(0..=16u8)? as usize;\n    let values: Vec<Option<String>> = (0..val_count)\n        .map(|_| -> Result<_> {\n            if u.arbitrary()? {\n                Ok(Some(bounded_string(u, 64)?))\n            } else {\n                Ok(None)\n            }\n        })\n        .collect::<Result<_>>()?;\n    Ok(values)\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/geometry/encode.rs",
    "content": "use std::collections::HashMap;\nuse std::mem;\n\nuse geo_types::Coord;\nuse probabilistic_collections::SipHasherBuilder;\nuse probabilistic_collections::hyperloglog::HyperLogLog;\n\nuse super::model::VertexBufferType;\nuse crate::MltResult;\nuse crate::codecs::hilbert::hilbert_sort_key;\nuse crate::codecs::zigzag::encode_componentwise_delta_vec2s;\nuse crate::decoder::GeometryType::{LineString, Point, Polygon};\nuse crate::decoder::{\n    ColumnType, DictionaryType, GeometryType, GeometryValues, LengthType, LogicalEncoding, Morton,\n    OffsetType, PhysicalEncoding, StreamMeta, StreamType,\n};\nuse crate::encoder::model::{CurveParams, StreamCtx};\nuse crate::encoder::{Codecs, Encoder, PhysicalCodecs, write_stream_payload};\nuse crate::utils::AsUsize as _;\n\n/// Compute `ZOrderCurve` parameters from the vertex value range.\n///\n/// Returns `(bits, shift)` matching Java's `SpaceFillingCurve`.\n/// Build a sorted unique Morton dictionary and per-vertex offset indices from a flat\n/// `[x0, y0, x1, y1, …]` vertex slice.\n///\n/// Returns `(sorted_unique_codes, per_vertex_offsets)`.\n#[hotpath::measure]\nfn build_morton_dict(vertices: &[i32], meta: Morton) -> MltResult<(Vec<u32>, Vec<u32>)> {\n    let codes: Vec<u32> = vertices\n        .chunks_exact(2)\n        .map(|c| meta.encode_morton(c[0], c[1]))\n        .collect::<Result<_, _>>()?;\n\n    let mut dict = codes.clone();\n    dict.sort_unstable();\n    dict.dedup();\n\n    #[expect(\n        clippy::cast_possible_truncation,\n        reason = \"dict.len() <= u32::MAX (deduped u32 codes)\"\n    )]\n    let code_to_idx: HashMap<u32, u32> = dict\n        .iter()\n        .enumerate()\n        .map(|(i, &c)| (c, i as u32))\n        .collect();\n    let offsets: Vec<u32> = codes.iter().map(|code| code_to_idx[code]).collect();\n\n    Ok((dict, offsets))\n}\n\n/// Build a Hilbert-curve-sorted unique vertex dictionary into caller-provided\n/// scratch.\n///\n/// On return, `dict_xy` holds the deduplicated `[x, y, …]` dictionary in\n/// Hilbert order and `offsets[i]` is the slot of input vertex `i`; `indexed`\n/// and `remap` are left as opaque scratch.\n///\n/// Dedup is keyed on the Hilbert curve index. Inside the `params.bits` grid\n/// the index ↔ `(x, y)` mapping is bijective, so dedup-by-index is equivalent\n/// to dedup-by-coordinate without the cost of hashing pairs.\n#[hotpath::measure]\nfn build_hilbert_dict(\n    vertices: &[i32],\n    params: CurveParams,\n    offsets: &mut Vec<u32>,\n    indexed: &mut Vec<u64>,\n    dict_xy: &mut Vec<i32>,\n    remap: &mut HashMap<u32, u32>,\n) {\n    offsets.clear();\n    indexed.clear();\n    dict_xy.clear();\n    remap.clear();\n\n    let coord_count = vertices.len() / 2;\n    if coord_count == 0 {\n        return;\n    }\n    offsets.reserve(coord_count);\n    indexed.reserve(coord_count);\n    dict_xy.reserve(coord_count * 2);\n    remap.reserve(coord_count);\n\n    for (i, c) in vertices.chunks_exact(2).enumerate() {\n        let k = hilbert_sort_key(Coord { x: c[0], y: c[1] }, params);\n        offsets.push(k);\n        // Key in the high 32 bits so a single u64 sort orders by Hilbert\n        // index while preserving the original position for tie-breaking.\n        let packed = (u64::from(k) << 32) | (i as u64);\n        indexed.push(packed);\n    }\n    indexed.sort_unstable();\n\n    let mut last_key: Option<u32> = None;\n    for &packed in &*indexed {\n        let key = (packed >> 32) as u32;\n        let src_idx = (packed & 0xFFFF_FFFF) as usize;\n        if last_key != Some(key) {\n            #[expect(\n                clippy::cast_possible_truncation,\n                reason = \"dict.len() <= coord_count <= u32::MAX\"\n            )]\n            let slot = (dict_xy.len() / 2) as u32;\n            dict_xy.push(vertices[src_idx * 2]);\n            dict_xy.push(vertices[src_idx * 2 + 1]);\n            remap.insert(key, slot);\n            last_key = Some(key);\n        }\n    }\n\n    for k in offsets.iter_mut() {\n        *k = remap[k];\n    }\n}\n\n/// Push consecutive offset-differences from `offsets` onto `lengths`.\n///\n/// Expects a slice of `n + 1` elements and produces `n` lengths,\n/// one per consecutive pair: `offsets[i + 1] - offsets[i]`.\n#[inline]\nfn extend_offsets(lengths: &mut Vec<u32>, offsets: &[u32]) -> usize {\n    lengths.extend(offsets.windows(2).map(|w| w[1] - w[0]));\n    offsets.len() - 1\n}\n\n/// Convert geometry offsets to length stream for encoding.\n/// This is the inverse of `decode_root_length_stream`.\n///\n/// The offset array can be either:\n/// - Sparse: entries only for geometries that need them (types > `buffer_id`), N+1 entries for N matching geoms\n/// - Dense (normalized): N+1 entries for N geometry types, indexed by geometry position\n///\n/// If dense `(len == geom_types.len() + 1)`, use geometry index directly.\n/// If sparse, use sequential indexing for matching geometry types.\nfn encode_root_length_stream(\n    geom_types: &[GeometryType],\n    geom_offsets: &[u32],\n    buffer_id: GeometryType,\n) -> Vec<u32> {\n    if geom_offsets.len() == geom_types.len() + 1 {\n        // Dense: zip by position, then filter out non-contributing types.\n        geom_types\n            .iter()\n            .zip(geom_offsets.windows(2))\n            .filter(|&(&t, _)| t > buffer_id)\n            .map(|(_, w)| w[1] - w[0])\n            .collect()\n    } else {\n        // Sparse: filter types first, then zip with consecutive offset pairs.\n        geom_types\n            .iter()\n            .filter(|&&t| t > buffer_id)\n            .zip(geom_offsets.windows(2))\n            .map(|(_, w)| w[1] - w[0])\n            .collect()\n    }\n}\n\n/// Convert part offsets to length stream for level 1 encoding.\nfn encode_level1_length_stream(\n    geom_types: &[GeometryType],\n    geom_offsets: &[u32],\n    part_offsets: &[u32],\n    is_line_string_present: bool,\n) -> Vec<u32> {\n    let mut lengths = Vec::new();\n    let mut part_idx = 0;\n\n    for (i, &geom_type) in geom_types.iter().enumerate() {\n        if geom_type.is_polygon() || (is_line_string_present && geom_type.is_linestring()) {\n            let n = (geom_offsets[i + 1] - geom_offsets[i]).as_usize();\n            part_idx += extend_offsets(&mut lengths, &part_offsets[part_idx..=part_idx + n]);\n        }\n        // Note: Point/MultiPoint don't have entries in the sparse part_offsets used\n        // at this call site, so part_idx must not advance for non-length types here.\n    }\n\n    lengths\n}\n\n/// Compute ring vertex-count lengths for the no-geometry-offsets + has-ring-offsets case.\n///\n/// In this branch `part_offsets` is a **dense** N+1 array (one slot per geometry,\n/// including Points) and `ring_offsets` holds the vertex offsets for every slot.\n/// Using the geometry index directly as the ring-slot index avoids the\n/// running-counter misalignment that `encode_level1_length_stream` would produce\n/// when non-length types (Points) occupy slots that a sparse counter skips.\nfn encode_ring_lengths_for_mixed(\n    geom_types: &[GeometryType],\n    part_offsets: &[u32],\n    ring_offsets: &[u32],\n    has_line_string: bool,\n) -> Vec<u32> {\n    let mut lengths = Vec::new();\n    for (i, &geom_type) in geom_types.iter().enumerate() {\n        if geom_type.is_polygon() || (has_line_string && geom_type.is_linestring()) {\n            let s = part_offsets[i].as_usize();\n            let e = part_offsets[i + 1].as_usize();\n            extend_offsets(&mut lengths, &ring_offsets[s..=e]);\n        }\n    }\n    lengths\n}\n\n/// Convert ring offsets to length stream for level 2 encoding.\n/// This is the inverse of `decode_level2_length_stream`.\n///\n/// The `geom_offsets` array is expected to be an N+1 element array for N geometries.\n/// The `part_offsets` array tracks ring counts cumulatively.\nfn encode_level2_length_stream(\n    geom_types: &[GeometryType],\n    geom_offsets: &[u32],\n    part_offsets: &[u32],\n    ring_offsets: &[u32],\n) -> Vec<u32> {\n    let mut lengths = Vec::new();\n    let mut part_idx = 0;\n    let mut ring_idx = 0;\n\n    for (i, &geom_type) in geom_types.iter().enumerate() {\n        let count = (geom_offsets[i + 1] - geom_offsets[i]).as_usize();\n\n        // Only Polygon and MultiPolygon have ring data in level 2\n        // LineStrings with Polygon present add their vertex counts directly to ring_offsets,\n        // but they don't have parts (ring count per linestring is always 1 implicitly)\n        if geom_type.is_polygon() {\n            // Polygon/MultiPolygon: iterate through sub-polygons, each has parts (ring counts)\n            for _ in 0..count {\n                let n = (part_offsets[part_idx + 1] - part_offsets[part_idx]).as_usize();\n                ring_idx += extend_offsets(&mut lengths, &ring_offsets[ring_idx..=ring_idx + n]);\n                part_idx += 1;\n            }\n        } else if geom_type.is_linestring() {\n            // LineStrings contribute to ring_offsets directly (vertex counts)\n            ring_idx += extend_offsets(&mut lengths, &ring_offsets[ring_idx..=ring_idx + count]);\n        }\n        // Note: Point/MultiPoint don't contribute to ring_offsets\n    }\n\n    lengths\n}\n\n/// Convert part offsets without ring buffer to length stream.\n///\n/// This path is reached only when `ring_offsets` is absent, which means no Polygon/MultiPolygon\n/// types are present (they always create `ring_offsets`).  Only LineString/MultiLineString\n/// contribute vertex-count lengths here; Point/MultiPoint use an implicit count of 1 in the\n/// decoder and produce no entry in this stream.\nfn encode_level1_without_ring_buffer_length_stream(\n    geom_types: &[GeometryType],\n    geom_offsets: &[u32],\n    part_offsets: &[u32],\n) -> Vec<u32> {\n    let mut lengths = Vec::new();\n    let mut part_idx = 0;\n\n    for (i, &geom_type) in geom_types.iter().enumerate() {\n        if geom_type.is_linestring() {\n            let n = (geom_offsets[i + 1] - geom_offsets[i]).as_usize();\n            part_idx += extend_offsets(&mut lengths, &part_offsets[part_idx..=part_idx + n]);\n        }\n        // Point/MultiPoint don't contribute to part_offsets; part_idx must not advance.\n    }\n\n    lengths\n}\n\n/// Normalize `geom_offsets` for mixed geometry types.\nfn normalize_geometry_offsets(vector_types: &[GeometryType], geom_offsets: &[u32]) -> Vec<u32> {\n    let mut normalized = Vec::with_capacity(vector_types.len() + 1);\n    let mut offset = 0_u32;\n    let mut sparse_idx = 0_usize; // Index into sparse geom_offsets\n\n    for &geom_type in vector_types {\n        normalized.push(offset);\n\n        if geom_type.is_multi() {\n            // Multi* types get their count from the sparse array\n            if sparse_idx + 1 < geom_offsets.len() {\n                let start = geom_offsets[sparse_idx];\n                let end = geom_offsets[sparse_idx + 1];\n                offset += end - start;\n                sparse_idx += 1;\n            }\n        } else {\n            // Non-Multi types have implicit count of 1\n            offset += 1;\n        }\n    }\n\n    normalized.push(offset);\n    normalized\n}\n\n/// Normalize `part_offsets` for ring-based indexing (Polygon mixed with `Point`/`LineString`).\n///\n/// Called only when `geom_offsets` is absent (no Multi\\* types) and `ring_offsets` is\n/// present.  In this context `part_offsets` is a compact polygon-only array; this function\n/// expands it to a dense per-geometry array so that `encode_ring_lengths_for_mixed` can index\n/// directly by geometry position.\n///\n/// Each slot in the output holds the first index into `ring_offsets` for that geometry:\n/// - `Point`: no contribution — slot range is empty (`ring_idx` unchanged).\n/// - `LineString`: contributes 1 slot (vertex count) — slot range is 1.\n/// - `Polygon`: contributes `ring_count` slots — slot range equals its ring count.\nfn normalize_part_offsets_for_rings(\n    vector_types: &[GeometryType],\n    part_offsets: &[u32],\n    ring_offsets: &[u32],\n) -> Vec<u32> {\n    let mut normalized = Vec::with_capacity(vector_types.len() + 1);\n    let mut ring_idx = 0_u32;\n    let mut part_idx = 0_usize;\n\n    for &geom_type in vector_types {\n        normalized.push(ring_idx);\n\n        if geom_type == Point {\n            // Point has no vertex-count slot in ring_offsets.\n        } else if geom_type.is_linestring() {\n            // Each LineString occupies exactly one slot in ring_offsets.\n            ring_idx += 1;\n        } else if geom_type.is_polygon() && part_idx + 1 < part_offsets.len() {\n            // Polygon occupies ring_count slots (one vertex-count per ring).\n            let ring_count = part_offsets[part_idx + 1] - part_offsets[part_idx];\n            ring_idx += ring_count;\n            part_idx += 1;\n        }\n        // No Multi* types can appear here (they always produce geom_offsets).\n    }\n\n    // ring_idx must equal ring_offsets.len() - 1 for well-formed data.\n    debug_assert_eq!(\n        ring_idx as usize,\n        ring_offsets.len().saturating_sub(1),\n        \"ring index mismatch after normalization\"\n    );\n    normalized.push(ring_idx);\n    normalized\n}\n\n/// Whether to race dictionary-based vertex layouts (Hilbert, Morton) against\n/// the plain Vec2 layout for this geometry column.\n///\n/// Profiling showed unconditional racing is ~2× slower overall: most layers\n/// have high vertex uniqueness, where the dict layouts cannot win and the\n/// extra sort + `HashMap` build is wasted. Gate on Morton fitting in 16 bits\n/// per axis (required by the spec) and on a `HyperLogLog`-estimated\n/// uniqueness ratio below the threshold.\n#[hotpath::measure]\nfn dict_may_be_beneficial(vertices: &[i32], enc: &Encoder) -> bool {\n    const MAXIMUM_UNIQUENESS_THRESHOLD_FOR_DICT: f64 = 0.66;\n\n    let coord_count = vertices.len() / 2;\n    if coord_count == 0 || enc.morton_cache.is_none() {\n        return false;\n    }\n\n    let mut hll = HyperLogLog::<Coord<i32>>::with_hasher(0.03, SipHasherBuilder::from_seed(0, 0));\n    for c in vertices.chunks_exact(2) {\n        hll.insert(&Coord::<i32> { x: c[0], y: c[1] });\n    }\n    #[expect(clippy::cast_precision_loss)]\n    let estimated_unique = hll.len().clamp(0.0, coord_count as f64);\n    #[expect(clippy::cast_precision_loss)]\n    let uniqueness_ratio = estimated_unique / coord_count as f64;\n    uniqueness_ratio < MAXIMUM_UNIQUENESS_THRESHOLD_FOR_DICT\n}\n\n/// Pre-populated by [`StagedLayer::encode_into`](crate::encoder::StagedLayer::encode_into);\n/// callers must have gated on [`dict_may_be_beneficial`] which rejects layers\n/// whose extent does not fit Morton.\nfn get_morton(enc: &Encoder) -> Morton {\n    enc.morton_cache.expect(\n        \"morton_cache populated by StagedLayer::encode_into; gated by dict_may_be_beneficial\",\n    )\n}\n\n/// Pre-populated by [`StagedLayer::encode_into`](crate::encoder::StagedLayer::encode_into).\nfn get_hilbert_params(enc: &Encoder) -> CurveParams {\n    enc.hilbert_cache\n        .expect(\"hilbert_cache populated by StagedLayer::encode_into\")\n}\n\n/// Encode the plain Vec2 vertex layout: componentwise-delta over the raw\n/// `[x0, y0, x1, y1, …]` slice.\nfn encode_vec2_vertex_stream(\n    vertices: &[i32],\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<u8> {\n    let delta = encode_componentwise_delta_vec2s(vertices, &mut codecs.logical.u32_tmp);\n    let ctx = StreamCtx::geom(StreamType::Data(DictionaryType::Vertex), \"vertex\");\n    let logical = LogicalEncoding::ComponentwiseDelta;\n    write_geo_precomputed_stream(delta, ctx, logical, enc, &mut codecs.physical)\n}\n\n/// Encode a Morton-keyed vertex dictionary: per-vertex offsets stream\n/// followed by a delta-encoded Morton-code dictionary.\nfn encode_morton_vertex_streams(\n    vertices: &[i32],\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<u8> {\n    let morton = get_morton(enc);\n    let (dict, offsets) = build_morton_dict(vertices, morton)?;\n    let mut n: u8 = 0;\n\n    let ctx = StreamCtx::geom(StreamType::Offset(OffsetType::Vertex), \"vertex_offsets\");\n    n += write_geo_u32_stream(&offsets, ctx, enc, codecs)?;\n\n    let delta = encode_morton_deltas(&dict, &mut codecs.logical.u32_tmp);\n    let ctx = StreamCtx::geom(StreamType::Data(DictionaryType::Morton), \"vertex\");\n    let logical = LogicalEncoding::MortonDelta(morton);\n    n += write_geo_precomputed_stream(delta, ctx, logical, enc, &mut codecs.physical)?;\n    Ok(n)\n}\n\n/// Encode a Hilbert-keyed vertex dictionary: per-vertex offsets stream\n/// followed by a componentwise-delta-encoded `[x, y, …]` dictionary in\n/// Hilbert order.\nfn encode_hilbert_vertex_streams(\n    vertices: &[i32],\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<u8> {\n    let params = get_hilbert_params(enc);\n    let mut n: u8 = 0;\n\n    // Take scratch ownership locally: `write_geo_*_stream` needs `&mut Codecs`,\n    // which would otherwise conflict with our `&[..]` views into these slots.\n    let mut offsets = mem::take(&mut codecs.logical.hilbert_offsets);\n    let mut indexed = mem::take(&mut codecs.logical.hilbert_indexed);\n    let mut dict_xy = mem::take(&mut codecs.logical.hilbert_dict_xy);\n    let mut remap = mem::take(&mut codecs.logical.hilbert_remap);\n\n    build_hilbert_dict(\n        vertices,\n        params,\n        &mut offsets,\n        &mut indexed,\n        &mut dict_xy,\n        &mut remap,\n    );\n    // Done with these — restore so the physical-encoding race below can use\n    // them via the codec.\n    codecs.logical.hilbert_indexed = indexed;\n    codecs.logical.hilbert_remap = remap;\n\n    let ctx = StreamCtx::geom(StreamType::Offset(OffsetType::Vertex), \"vertex_offsets\");\n    n += write_geo_u32_stream(&offsets, ctx, enc, codecs)?;\n\n    // Reuse `offsets` as the delta output rather than allocating another Vec;\n    // also keeps `codecs.logical.u32_values` free for the inner race.\n    encode_componentwise_delta_vec2s(&dict_xy, &mut offsets);\n    let ctx = StreamCtx::geom(StreamType::Data(DictionaryType::Vertex), \"vertex\");\n    let logical = LogicalEncoding::ComponentwiseDelta;\n    n += write_geo_precomputed_stream(&offsets, ctx, logical, enc, &mut codecs.physical)?;\n\n    codecs.logical.hilbert_offsets = offsets;\n    codecs.logical.hilbert_dict_xy = dict_xy;\n    Ok(n)\n}\n\n/// Write a geometry `u32` stream: [`Encoder::override_int_enc`] when explicit mode is active,\n/// otherwise try all pruned candidates and keep the shortest.\n///\n/// Returns `1` if the stream was written, `0` if it was skipped.  Empty streams are skipped\n/// unless [`Encoder::force_stream`] returns `true` for this stream's [`StreamCtx`].\nfn write_geo_u32_stream(\n    data: &[u32],\n    ctx: StreamCtx,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<u8> {\n    Ok(if data.is_empty() && !enc.force_stream(&ctx) {\n        0\n    } else {\n        codecs.write_int_stream(data, &ctx, enc)?;\n        1\n    })\n}\n\n/// Like [`write_geo_u32_stream`] but for pre-logically-encoded data: competes\n/// only the physical encoders instead of applying a logical transform.\n///\n/// Returns `1` if the stream was written, `0` if skipped (empty + no force).\nfn write_geo_precomputed_stream(\n    data: &[u32],\n    ctx: StreamCtx,\n    logical: LogicalEncoding,\n    enc: &mut Encoder,\n    physical: &mut PhysicalCodecs,\n) -> MltResult<u8> {\n    use PhysicalEncoding as PE;\n\n    Ok(if data.is_empty() && !enc.force_stream(&ctx) {\n        0\n    } else {\n        if let Some(int_enc) = enc.override_int_enc(&ctx) {\n            physical.write_encoded_as::<[u32]>(&ctx, enc, logical, data, int_enc.physical)?;\n        } else if data.is_empty() {\n            let meta = StreamMeta::new2(ctx.stream_type, logical, PE::None, 0)?;\n            write_stream_payload(&mut enc.data, meta, false, &[])?;\n        } else {\n            let mut alt = enc.try_alternatives();\n            alt.with(|enc| {\n                let vals = physical.fastpfor(data)?;\n                let meta = StreamMeta::new2(ctx.stream_type, logical, PE::FastPFor256, data.len())?;\n                write_stream_payload(&mut enc.data, meta, false, vals)\n            })?;\n            alt.with(|enc| {\n                let vals = physical.varint(data);\n                let meta = StreamMeta::new2(ctx.stream_type, logical, PE::VarInt, data.len())?;\n                write_stream_payload(&mut enc.data, meta, false, vals)\n            })?;\n        }\n        1\n    })\n}\n\nimpl GeometryValues {\n    /// Write the geometry column to `enc`.\n    #[hotpath::measure]\n    pub fn write_to(self, enc: &mut Encoder, codecs: &mut Codecs) -> MltResult<()> {\n        let Self {\n            vector_types,\n            geometry_offsets,\n            part_offsets,\n            ring_offsets,\n            index_buffer,\n            triangles,\n            vertices,\n        } = self;\n\n        // Flatten every Option<Vec> → Vec  (empty == not present).\n        // triangles: None means no tessellation; Some([]) can't occur in practice (each\n        // push_geom appends a count), so empty == absent is safe here too.\n        // vertices: None means no coordinate data (e.g. empty layer).\n        let geom_offsets = geometry_offsets.unwrap_or_default();\n        let part_offsets = part_offsets.unwrap_or_default();\n        let ring_offsets = ring_offsets.unwrap_or_default();\n        let index_buffer = index_buffer.unwrap_or_default();\n        let triangles = triangles.unwrap_or_default();\n        let vertices = vertices.unwrap_or_default();\n\n        // Direct callers (tests, custom drivers) skip `StagedLayer::encode_into`\n        // and arrive with empty caches; populate from `vertices` so the\n        // dictionary builders can rely on them unconditionally.\n        if enc.hilbert_cache.is_none() {\n            enc.hilbert_cache = Some(CurveParams::from_vertices(&vertices));\n        }\n        if enc.morton_cache.is_none() {\n            let p = enc.hilbert_cache.expect(\"populated above\");\n            enc.morton_cache = Morton::new(p.bits, p.shift).ok();\n        }\n\n        let meta: Vec<u32> = vector_types.iter().map(|t| *t as u32).collect();\n\n        let part_offsets = if geom_offsets.is_empty()\n            && !ring_offsets.is_empty()\n            && !part_offsets.is_empty()\n            && part_offsets.len() != vector_types.len() + 1\n        {\n            // Normalize part_offsets when there are no geometry offsets but ring offsets exist.\n            normalize_part_offsets_for_rings(&vector_types, &part_offsets, &ring_offsets)\n        } else {\n            part_offsets\n        };\n\n        // Write column type to meta; reserve exactly 1 byte for stream count\n        // (geometry never exceeds ~8 streams, always fits in a single varint byte).\n        enc.write_column_type(ColumnType::Geometry)?;\n        let stream_count_pos = enc.data.len();\n        enc.data.push(0); // placeholder — patched below\n        let mut n: u8 = 0;\n\n        // Meta stream — always written, even for a zero-feature layer.\n        let ctx = StreamCtx::geom(StreamType::Length(LengthType::VarBinary), \"meta\");\n        codecs.write_int_stream(&meta, &ctx, enc)?;\n        n += 1;\n\n        // Topology: compute each length stream and write it immediately.\n        if !geom_offsets.is_empty() {\n            let geom_offsets = if geom_offsets.len() == vector_types.len() + 1 {\n                geom_offsets\n            } else {\n                normalize_geometry_offsets(&vector_types, &geom_offsets)\n            };\n            let data = encode_root_length_stream(&vector_types, &geom_offsets, Polygon);\n            let ctx = StreamCtx::geom(StreamType::Length(LengthType::Geometries), \"geometries\");\n            n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n\n            // part_offsets is intentionally kept sparse here (polygon-only cumulative\n            // ring counts). encode_level1/2_length_stream navigate it with a running\n            // part_idx counter that advances only for Polygon/LineString types, which\n            // matches the sparse layout. Densifying via normalize_part_offsets_for_rings\n            // would insert Point slots and corrupt the counter arithmetic.\n            if !part_offsets.is_empty() {\n                if ring_offsets.is_empty() {\n                    // geom → parts only (no rings).\n                    let data = encode_level1_without_ring_buffer_length_stream(\n                        &vector_types,\n                        &geom_offsets,\n                        &part_offsets,\n                    );\n                    let ctx = StreamCtx::geom(StreamType::Length(LengthType::Parts), \"no_rings\");\n                    n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n                } else {\n                    // Full topology: geom → parts → rings.\n                    // LineStrings contribute to rings here, not to parts.\n                    let data = encode_level1_length_stream(\n                        &vector_types,\n                        &geom_offsets,\n                        &part_offsets,\n                        false,\n                    );\n                    let ctx = StreamCtx::geom(StreamType::Length(LengthType::Parts), \"rings\");\n                    n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n\n                    let data = encode_level2_length_stream(\n                        &vector_types,\n                        &geom_offsets,\n                        &part_offsets,\n                        &ring_offsets,\n                    );\n                    let ctx = StreamCtx::geom(StreamType::Length(LengthType::Rings), \"rings2\");\n                    n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n                }\n            }\n        } else if !part_offsets.is_empty() {\n            if ring_offsets.is_empty() {\n                let data = encode_root_length_stream(&vector_types, &part_offsets, Point);\n                let ctx = StreamCtx::geom(StreamType::Length(LengthType::Parts), \"no_rings\");\n                n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n            } else {\n                // No Multi* types; parts → rings (Polygon / mixed Point+Polygon).\n                // Java writes an empty GEOMETRIES stream here for tessellated polygons; only do\n                // so when explicitly forced (e.g. to preserve byte-for-byte Java compatibility).\n                let ctx = StreamCtx::geom(StreamType::Length(LengthType::Geometries), \"geometries\");\n                n += write_geo_u32_stream(&[], ctx, enc, codecs)?;\n\n                let data = encode_root_length_stream(&vector_types, &part_offsets, LineString);\n                let ctx = StreamCtx::geom(StreamType::Length(LengthType::Parts), \"parts\");\n                n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n\n                // part_offs is a dense N+1 array (one slot per geometry incl. Points);\n                // ring_offs stores vertex offsets per slot.  The dense-aware helper skips\n                // Point slots by index rather than a running counter.\n                let has_line_string = vector_types\n                    .iter()\n                    .copied()\n                    .any(GeometryType::is_linestring);\n                let data = encode_ring_lengths_for_mixed(\n                    &vector_types,\n                    &part_offsets,\n                    &ring_offsets,\n                    has_line_string,\n                );\n                let ctx = StreamCtx::geom(StreamType::Length(LengthType::Rings), \"parts_ring\");\n                n += write_geo_u32_stream(&data, ctx, enc, codecs)?;\n            }\n        }\n\n        let ctx = StreamCtx::geom(StreamType::Length(LengthType::Triangles), \"triangles\");\n        n += write_geo_u32_stream(&triangles, ctx, enc, codecs)?;\n        let ctx = StreamCtx::geom(StreamType::Offset(OffsetType::Index), \"triangles_indexes\");\n        n += write_geo_u32_stream(&index_buffer, ctx, enc, codecs)?;\n\n        if let Some(forced) = enc.override_vertex_buffer_type() {\n            n += match forced {\n                VertexBufferType::Vec2 => encode_vec2_vertex_stream(&vertices, enc, codecs)?,\n                VertexBufferType::Morton => encode_morton_vertex_streams(&vertices, enc, codecs)?,\n                VertexBufferType::Hilbert => encode_hilbert_vertex_streams(&vertices, enc, codecs)?,\n            };\n        } else if dict_may_be_beneficial(&vertices, enc) {\n            // Morton fits (the gate above ensures it), so race all three.\n            let mut winner_size: usize = usize::MAX;\n            let mut winner_stream_cnt: u8 = 0;\n            let mut alt = enc.try_alternatives();\n            alt.with(|e| {\n                let ds = e.data.len();\n                let ms = e.meta.len();\n                winner_stream_cnt = encode_vec2_vertex_stream(&vertices, e, codecs)?;\n                winner_size = (e.data.len() - ds) + (e.meta.len() - ms);\n                Ok(())\n            })?;\n            alt.with(|e| {\n                let ds = e.data.len();\n                let ms = e.meta.len();\n                let cnt = encode_hilbert_vertex_streams(&vertices, e, codecs)?;\n                let size = (e.data.len() - ds) + (e.meta.len() - ms);\n                if size < winner_size {\n                    winner_stream_cnt = cnt;\n                    winner_size = size;\n                }\n                Ok(())\n            })?;\n            alt.with(|e| {\n                let ds = e.data.len();\n                let ms = e.meta.len();\n                let cnt = encode_morton_vertex_streams(&vertices, e, codecs)?;\n                let size = (e.data.len() - ds) + (e.meta.len() - ms);\n                if size < winner_size {\n                    winner_stream_cnt = cnt;\n                }\n                Ok(())\n            })?;\n            drop(alt);\n            n += winner_stream_cnt;\n        } else {\n            n += encode_vec2_vertex_stream(&vertices, enc, codecs)?;\n        }\n\n        // Patch the reserved stream-count byte.\n        debug_assert!(n <= 127, \"geometry stream count must fit in one byte\");\n        enc.data[stream_count_pos] = n;\n        Ok(())\n    }\n}\n\nfn encode_morton_deltas<'a>(codes: &[u32], buffer: &'a mut Vec<u32>) -> &'a mut Vec<u32> {\n    buffer.clear();\n    if let Some(&first) = codes.first() {\n        buffer.reserve(codes.len());\n        buffer.extend(std::iter::once(first).chain(codes.windows(2).map(|w| w[1] - w[0])));\n    }\n    buffer\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_build_morton_dict() {\n        let meta = Morton { bits: 4, shift: 0 };\n        // vertices: [x0,y0, x1,y1, x2,y2, x3,y3] — repeat (1,2) to test dedup\n        let vertices = [1, 2, 3, 4, 1, 2, 0, 0];\n        let (dict, offsets) = build_morton_dict(&vertices, meta).unwrap();\n\n        assert!(\n            dict.windows(2).all(|w| w[0] < w[1]),\n            \"dict not sorted/unique\"\n        );\n        assert_eq!(offsets.len(), 4, \"offsets length == number of vertex pairs\");\n        assert_eq!(offsets[0], offsets[2], \"duplicate (1,2) should share index\");\n        assert!(offsets.iter().all(|&o| (o as usize) < dict.len()));\n    }\n\n    #[test]\n    fn test_encode_root_length_stream() {\n        // Single Polygon geometry (no Multi)\n        let types = vec![Polygon];\n        let offsets = vec![0, 1]; // One polygon\n\n        let lengths = encode_root_length_stream(&types, &offsets, Polygon);\n        // Polygon == buffer_id, so no length encoded\n        assert!(lengths.is_empty());\n\n        // MultiPolygon needs length encoded\n        let types = vec![GeometryType::MultiPolygon];\n        let offsets = vec![0, 2]; // MultiPolygon with 2 polygons\n\n        let lengths = encode_root_length_stream(&types, &offsets, Polygon);\n        assert_eq!(lengths, vec![2]);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/geometry/geotype.rs",
    "content": "use geo::{Convert as _, TriangulateEarcut as _};\nuse geo_types::{Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Polygon};\n\nuse crate::decoder::{GeometryType, GeometryValues};\n\nimpl TryFrom<&Geometry<i32>> for GeometryType {\n    type Error = ();\n\n    fn try_from(geom: &Geometry<i32>) -> Result<Self, Self::Error> {\n        Ok(match geom {\n            Geometry::<i32>::Point(_) => Self::Point,\n            Geometry::<i32>::MultiPoint(_) => Self::MultiPoint,\n            Geometry::<i32>::LineString(_) => Self::LineString,\n            Geometry::<i32>::MultiLineString(_) => Self::MultiLineString,\n            Geometry::<i32>::Polygon(_) => Self::Polygon,\n            Geometry::<i32>::MultiPolygon(_) => Self::MultiPolygon,\n            Geometry::<i32>::Line(_)\n            | Geometry::<i32>::GeometryCollection(_)\n            | Geometry::<i32>::Rect(_)\n            | Geometry::<i32>::Triangle(_) => {\n                return Err(());\n            }\n        })\n    }\n}\n\n/// Run the Earcut algorithm on `polygon`, append triangle indices (shifted by `vertex_offset`)\n/// into `index_buf`, and return `(num_triangles, num_vertices)`.\nfn earcut_into(polygon: &Polygon<i32>, vertex_offset: u32, index_buf: &mut Vec<u32>) -> (u32, u32) {\n    let polygon_f64: Polygon<f64> = polygon.convert();\n    let raw = polygon_f64.earcut_triangles_raw();\n    let num_triangles = u32::try_from(raw.triangle_indices.len() / 3).expect(\"too many triangles\");\n    let num_vertices = u32::try_from(raw.vertices.len()).expect(\"too many vertices\");\n\n    for i in raw.triangle_indices {\n        let base = u32::try_from(i).expect(\"mlt vertex index overflow\");\n        let idx = base\n            .checked_add(vertex_offset)\n            .expect(\"vertex index overflow\");\n        index_buf.push(idx);\n    }\n\n    (num_triangles, num_vertices)\n}\n\nimpl GeometryValues {\n    /// Returns a [`GeometryValues`] with an empty `triangles` buffer pre-initialized.\n    ///\n    /// When `triangles` is `Some`, polygon push methods automatically compute and store\n    /// Earcut tessellation data as geometries are added.\n    /// Use [`Self::default`] when tessellation is not required.\n    #[must_use]\n    pub fn new_tessellated() -> Self {\n        Self {\n            triangles: Some(vec![]),\n            ..Default::default()\n        }\n    }\n\n    /// Tessellate `polygon` using the Earcut algorithm and append the results directly into\n    /// `self.index_buffer` and `self.triangles`.\n    fn tessellate_polygon(&mut self, polygon: &Polygon<i32>) {\n        if let Some(triangles) = self.triangles.as_mut() {\n            let (num_triangles, _) =\n                earcut_into(polygon, 0, self.index_buffer.get_or_insert_with(Vec::new));\n            triangles.push(num_triangles);\n        }\n    }\n\n    /// Tessellate all polygons in `mp` and append the combined results into\n    /// `self.index_buffer` and `self.triangles`.\n    ///\n    /// Indices for each constituent polygon are offset by the cumulative vertex count of all\n    /// preceding polygons so they reference the correct positions in the shared vertex buffer.\n    /// A single total triangle count (summed over all constituent polygons) is pushed into\n    /// `self.triangles`.\n    fn tessellate_multi_polygon(&mut self, mp: &MultiPolygon<i32>) {\n        if let Some(triangles) = self.triangles.as_mut() {\n            let mut total_triangles = 0u32;\n            let mut vertex_offset = 0u32;\n            let index_buffer = self.index_buffer.get_or_insert_with(Vec::new);\n            for poly in &mp.0 {\n                let (num_triangles, num_verts) = earcut_into(poly, vertex_offset, index_buffer);\n                total_triangles += num_triangles;\n                vertex_offset += num_verts;\n            }\n            triangles.push(total_triangles);\n        }\n    }\n\n    /// Add a geometry to this decoded geometry collection.\n    /// This is the reverse of `to_geojson` - it converts a `&Geometry<i32>`\n    /// into the internal MLT representation with offset arrays.\n    #[must_use]\n    pub fn with_geom(mut self, geom: &Geometry<i32>) -> Self {\n        self.push_geom(geom);\n        self\n    }\n\n    /// Add a geometry to this decoded geometry collection (mutable version).\n    pub fn push_geom(&mut self, geom: &Geometry<i32>) {\n        match geom {\n            Geometry::<i32>::Point(p) => self.push_point(p.0),\n            Geometry::<i32>::Line(l) => self.push_linestring(&LineString(vec![l.start, l.end])),\n            Geometry::<i32>::LineString(ls) => self.push_linestring(ls),\n            Geometry::<i32>::Polygon(p) => self.push_polygon(p),\n            Geometry::<i32>::MultiPoint(mp) => self.push_multi_point(mp),\n            Geometry::<i32>::MultiLineString(mls) => self.push_multi_linestring(mls),\n            Geometry::<i32>::MultiPolygon(mp) => self.push_multi_polygon(mp),\n            Geometry::<i32>::Triangle(t) => self.push_polygon(&t.to_polygon()),\n            Geometry::<i32>::Rect(r) => self.push_polygon(&r.to_polygon()),\n            Geometry::<i32>::GeometryCollection(gc) => {\n                for g in gc {\n                    self.push_geom(g);\n                }\n            }\n        }\n    }\n\n    fn push_point(&mut self, coord: Coord<i32>) {\n        self.vector_types.push(GeometryType::Point);\n        self.vertices\n            .get_or_insert_with(Vec::new)\n            .extend([coord.x, coord.y]);\n    }\n\n    fn push_linestring(&mut self, ls: &LineString<i32>) {\n        self.vector_types.push(GeometryType::LineString);\n\n        let verts = self.vertices.get_or_insert_with(Vec::new);\n        // If ring_offsets exists (i.e., there's a Polygon in the layer),\n        // add LineString vertex count to ring_offsets instead of part_offsets.\n        // This matches Java's behavior where LineString adds to numRings when containsPolygon.\n        let offsets = self\n            .ring_offsets\n            .as_mut()\n            .unwrap_or_else(|| self.part_offsets.get_or_insert_with(Vec::new));\n\n        push_linestrings(std::iter::once(ls), verts, offsets);\n    }\n\n    fn push_polygon(&mut self, poly: &Polygon<i32>) {\n        // Only on the very first polygon: if LineStrings were pushed before us,\n        // their vertex offsets are sitting in part_offsets. Move them to\n        // ring_offsets now, before we set up ring_offsets for polygon use.\n        // On subsequent polygons ring_offsets is already initialized and\n        // part_offsets holds polygon ring-range data — leave both alone.\n        self.vector_types.push(GeometryType::Polygon);\n        self.init_polygon_offsets();\n\n        let verts = self.vertices.get_or_insert_with(Vec::new);\n        let rings = self.ring_offsets.as_mut().unwrap();\n        let parts = self.part_offsets.as_mut().unwrap();\n\n        push_polygon_rings(poly, verts, rings, parts);\n        self.tessellate_polygon(poly);\n    }\n\n    /// Initialize offset arrays for polygon storage. On the first polygon,\n    /// moves any `LineString` vertex offsets from `part_offsets` to `ring_offsets`.\n    fn init_polygon_offsets(&mut self) {\n        if self.ring_offsets.is_none()\n            && let Some(ls_parts) = self.part_offsets.take()\n        {\n            self.ring_offsets = Some(ls_parts);\n        }\n        init_offsets(self.ring_offsets.get_or_insert_with(Vec::new));\n        init_offsets(self.part_offsets.get_or_insert_with(Vec::new));\n    }\n\n    fn push_multi_point(&mut self, mp: &MultiPoint<i32>) {\n        self.vector_types.push(GeometryType::MultiPoint);\n\n        let verts = self.vertices.get_or_insert_with(Vec::new);\n        for point in mp {\n            verts.extend([point.0.x, point.0.y]);\n        }\n\n        self.push_geometry_count(u32::try_from(mp.0.len()).expect(\"point count overflow\"));\n    }\n\n    fn push_multi_linestring(&mut self, mls: &MultiLineString<i32>) {\n        self.vector_types.push(GeometryType::MultiLineString);\n\n        let verts = self.vertices.get_or_insert_with(Vec::new);\n        // When a Polygon is present (ring_offsets exists), LineString vertex counts\n        // go to ring_offsets instead of part_offsets. This matches Java's behavior.\n        let offsets = self\n            .ring_offsets\n            .as_mut()\n            .unwrap_or_else(|| self.part_offsets.get_or_insert_with(Vec::new));\n\n        push_linestrings(mls.iter(), verts, offsets);\n\n        self.push_geometry_count(u32::try_from(mls.0.len()).expect(\"linestring count overflow\"));\n    }\n\n    fn push_multi_polygon(&mut self, mp: &MultiPolygon<i32>) {\n        self.vector_types.push(GeometryType::MultiPolygon);\n        self.init_polygon_offsets();\n\n        let verts = self.vertices.get_or_insert_with(Vec::new);\n        let rings = self.ring_offsets.as_mut().unwrap();\n        let parts = self.part_offsets.as_mut().unwrap();\n\n        for poly in mp {\n            push_polygon_rings(poly, verts, rings, parts);\n        }\n\n        self.push_geometry_count(u32::try_from(mp.0.len()).expect(\"polygon count overflow\"));\n        self.tessellate_multi_polygon(mp);\n    }\n\n    /// Initialize and update `geometry_offsets` with a sub-geometry count.\n    fn push_geometry_count(&mut self, count: u32) {\n        let g = self.geometry_offsets.get_or_insert_with(Vec::new);\n        init_offsets(g);\n        g.push(g.last().unwrap() + count);\n    }\n}\n\n/// Ensure offset array starts with 0.\nfn init_offsets(v: &mut Vec<u32>) {\n    if v.is_empty() {\n        v.push(0);\n    }\n}\n\n/// Push a single polygon's rings (exterior + interiors) to the offset arrays.\n/// MLT omits closing vertices, so we strip them if present.\nfn push_polygon_rings(\n    poly: &Polygon<i32>,\n    verts: &mut Vec<i32>,\n    rings: &mut Vec<u32>,\n    parts: &mut Vec<u32>,\n) {\n    let mut ring_count = *parts.last().unwrap();\n    for ring in std::iter::once(poly.exterior()).chain(poly.interiors()) {\n        push_ring(ring, verts, rings);\n        ring_count += 1;\n    }\n    parts.push(ring_count);\n}\n\n/// Push a ring's coordinates (stripping closing vertex) to verts and update rings offset.\nfn push_ring(ring: &LineString<i32>, verts: &mut Vec<i32>, rings: &mut Vec<u32>) {\n    let coords = &ring.0;\n    let len = if coords.len() > 1 && coords.last() == coords.first() {\n        coords.len() - 1\n    } else {\n        coords.len()\n    };\n    for c in &coords[..len] {\n        verts.extend([c.x, c.y]);\n    }\n    let prev = *rings.last().unwrap();\n    rings.push(prev + u32::try_from(len).expect(\"vertex count overflow\"));\n}\n\n/// Push linestrings to vertex buffer and offset array.\nfn push_linestrings<'a>(\n    iter: impl Iterator<Item = &'a LineString<i32>>,\n    verts: &mut Vec<i32>,\n    offsets: &mut Vec<u32>,\n) {\n    init_offsets(offsets);\n    for ls in iter {\n        for c in ls.coords() {\n            verts.extend([c.x, c.y]);\n        }\n        let prev = *offsets.last().unwrap();\n        offsets.push(prev + u32::try_from(ls.0.len()).expect(\"vertex count overflow\"));\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use geo_types::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, wkt};\n    use insta::assert_snapshot;\n    use integer_encoding::VarInt;\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::__private::PhysicalEncoding;\n    use crate::LazyParsed;\n    use crate::decoder::{\n        DictionaryType, IntEncoding, LengthType, LogicalEncoding, Morton, OffsetType, RawGeometry,\n        StreamMeta, StreamType,\n    };\n    use crate::encoder::model::StreamCtx;\n    use crate::encoder::{Codecs, EncodedStream, Encoder, ExplicitEncoder, IntEncoder};\n    use crate::test_helpers::{assert_empty, dec, parser};\n    use crate::utils::BinarySerializer as _;\n\n    /// Encode, serialize, parse, and decode a `GeometryValues`.\n    /// The input must already be in the dense canonical form that `from_encoded`\n    /// produces (i.e. built via a previous `roundtrip` call, not via `push_*`).\n    fn roundtrip(decoded: &GeometryValues) -> GeometryValues {\n        let mut enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        decoded\n            .clone()\n            .write_to(&mut enc, &mut codecs)\n            .expect(\"Failed to encode\");\n\n        let parsed = assert_empty(RawGeometry::from_bytes(&enc.data, &mut parser()));\n\n        LazyParsed::Raw(parsed)\n            .into_parsed(&mut dec())\n            .expect(\"Failed to decode\")\n    }\n\n    /// Build a `GeometryValues` from a sequence of `geo_types::Geometry::<i32>` values via\n    /// `push_geom` and perform a two-cycle encode/decode:\n    ///\n    /// 1. push -> encode -> decode  (`canonical`): exercises `push_geom` and\n    ///    `normalize_geometry_offsets`; normalizes the sparse push_* layout to\n    ///    the dense form that `from_encoded` always returns.\n    /// 2. canonical -> encode -> decode  (`output`): verifies idempotency of\n    ///    encode/decode on the canonical form\n    ///\n    /// Comparing `canonical == output` catches both panics in the push path\n    /// and silent data corruption in encode/decode\n    fn roundtrip_via_push(geoms: &[Geometry<i32>]) -> (GeometryValues, GeometryValues) {\n        let mut pushed = GeometryValues::default();\n        for g in geoms {\n            pushed.push_geom(g);\n        }\n        let canonical = roundtrip(&pushed);\n        let output = roundtrip(&canonical);\n        (canonical, output)\n    }\n\n    fn arb_coord() -> impl Strategy<Value = Coord<i32>> {\n        (any::<i32>(), any::<i32>()).prop_map(|(x, y)| Coord::<i32> { x, y })\n    }\n\n    fn arb_geom() -> impl Strategy<Value = Geometry<i32>> {\n        prop_oneof![\n            // Point\n            arb_coord().prop_map(Point).prop_map(Geometry::<i32>::Point),\n            // LineString\n            prop::collection::vec(arb_coord(), 2..10)\n                .prop_map(|coords| Geometry::<i32>::LineString(LineString(coords))),\n            // Polygon (single exterior ring, no holes)\n            prop::collection::vec(arb_coord(), 3..8).prop_map(|mut coords| {\n                coords.push(coords[0]);\n                Geometry::<i32>::Polygon(Polygon::new(LineString(coords), vec![]))\n            }),\n            // MultiPoint\n            prop::collection::vec(arb_coord(), 2..8).prop_map(|coords| {\n                Geometry::<i32>::MultiPoint(MultiPoint(coords.into_iter().map(Point).collect()))\n            }),\n            // MultiLineString\n            prop::collection::vec(prop::collection::vec(arb_coord(), 2..6), 2..5,).prop_map(\n                |lines| Geometry::<i32>::MultiLineString(MultiLineString(\n                    lines.into_iter().map(LineString).collect(),\n                ))\n            ),\n            // MultiPolygon\n            prop::collection::vec(arb_coord(), 3..6).prop_map(|mut coords| {\n                coords.push(coords[0]);\n                Geometry::<i32>::MultiPolygon(MultiPolygon(vec![Polygon::new(\n                    LineString(coords),\n                    vec![],\n                )]))\n            }),\n        ]\n    }\n\n    /// Mixing `LineString` with `MultiLineString`\n    fn arb_mixed_linestring_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(arb_geom(), 2..12)\n            .prop_map(|geoms| {\n                geoms\n                    .into_iter()\n                    .filter(|g| {\n                        matches!(\n                            g,\n                            Geometry::<i32>::LineString(_) | Geometry::<i32>::MultiLineString(_)\n                        )\n                    })\n                    .collect::<Vec<_>>()\n            })\n            .prop_filter(\"needs both LS and MLS\", |geoms| {\n                geoms\n                    .iter()\n                    .any(|g| matches!(g, Geometry::<i32>::LineString(_)))\n                    && geoms\n                        .iter()\n                        .any(|g| matches!(g, Geometry::<i32>::MultiLineString(_)))\n            })\n    }\n\n    /// Mixing `Point` with `MultiPoint`\n    fn arb_mixed_point_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(arb_geom(), 2..12)\n            .prop_map(|geoms| {\n                geoms\n                    .into_iter()\n                    .filter(|g| {\n                        matches!(\n                            g,\n                            Geometry::<i32>::Point(_) | Geometry::<i32>::MultiPoint(_)\n                        )\n                    })\n                    .collect::<Vec<_>>()\n            })\n            .prop_filter(\"needs both P and MP\", |geoms| {\n                geoms.iter().any(|g| matches!(g, Geometry::<i32>::Point(_)))\n                    && geoms\n                        .iter()\n                        .any(|g| matches!(g, Geometry::<i32>::MultiPoint(_)))\n            })\n    }\n\n    /// Mixing `Polygon` with `MultiPolygon`\n    fn arb_mixed_polygon_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(arb_geom(), 2..8)\n            .prop_map(|geoms| {\n                geoms\n                    .into_iter()\n                    .filter(|g| {\n                        matches!(\n                            g,\n                            Geometry::<i32>::Polygon(_) | Geometry::<i32>::MultiPolygon(_)\n                        )\n                    })\n                    .collect::<Vec<_>>()\n            })\n            .prop_filter(\"needs both Poly and MPoly\", |geoms| {\n                geoms\n                    .iter()\n                    .any(|g| matches!(g, Geometry::<i32>::Polygon(_)))\n                    && geoms\n                        .iter()\n                        .any(|g| matches!(g, Geometry::<i32>::MultiPolygon(_)))\n            })\n    }\n\n    /// Mixing `Point` with `MultiLineString`\n    fn arb_cross_point_mls_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(\n            prop_oneof![\n                arb_coord().prop_map(Point).prop_map(Geometry::<i32>::Point),\n                prop::collection::vec(prop::collection::vec(arb_coord(), 2..6), 2..5).prop_map(\n                    |lines| {\n                        Geometry::<i32>::MultiLineString(MultiLineString(\n                            lines.into_iter().map(LineString).collect(),\n                        ))\n                    }\n                ),\n            ],\n            2..12,\n        )\n        .prop_filter(\"needs both Point and MultiLineString\", |geoms| {\n            geoms.iter().any(|g| matches!(g, Geometry::<i32>::Point(_)))\n                && geoms\n                    .iter()\n                    .any(|g| matches!(g, Geometry::<i32>::MultiLineString(_)))\n        })\n    }\n\n    /// Mixing `Point` with `MultiPolygon`.\n    fn arb_cross_point_mpoly_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(\n            prop_oneof![\n                arb_coord().prop_map(Point).prop_map(Geometry::<i32>::Point),\n                prop::collection::vec(arb_coord(), 3..6).prop_map(|mut coords| {\n                    coords.push(coords[0]);\n                    Geometry::<i32>::MultiPolygon(MultiPolygon(vec![Polygon::new(\n                        LineString(coords),\n                        vec![],\n                    )]))\n                }),\n            ],\n            2..10,\n        )\n        .prop_filter(\"needs both Point and MultiPolygon\", |geoms| {\n            geoms.iter().any(|g| matches!(g, Geometry::<i32>::Point(_)))\n                && geoms\n                    .iter()\n                    .any(|g| matches!(g, Geometry::<i32>::MultiPolygon(_)))\n        })\n    }\n\n    /// Mixing `LineString` with `MultiPolygon`\n    fn arb_cross_ls_mpoly_geoms() -> impl Strategy<Value = Vec<Geometry<i32>>> {\n        prop::collection::vec(\n            prop_oneof![\n                prop::collection::vec(arb_coord(), 2..8)\n                    .prop_map(|coords| Geometry::<i32>::LineString(LineString(coords))),\n                prop::collection::vec(arb_coord(), 3..6).prop_map(|mut coords| {\n                    coords.push(coords[0]);\n                    Geometry::<i32>::MultiPolygon(MultiPolygon(vec![Polygon::new(\n                        LineString(coords),\n                        vec![],\n                    )]))\n                }),\n            ],\n            2..10,\n        )\n        .prop_filter(\"needs both LineString and MultiPolygon\", |geoms| {\n            geoms\n                .iter()\n                .any(|g| matches!(g, Geometry::<i32>::LineString(_)))\n                && geoms\n                    .iter()\n                    .any(|g| matches!(g, Geometry::<i32>::MultiPolygon(_)))\n        })\n    }\n\n    proptest! {\n        #[test]\n        fn test_geometry_roundtrip(geom in arb_geom()) {\n            let (canonical, output) = roundtrip_via_push(&[geom]);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[test]\n        fn test_mixed_linestring_roundtrip(geoms in arb_mixed_linestring_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[test]\n        fn test_mixed_point_roundtrip(geoms in arb_mixed_point_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[test]\n        fn test_mixed_polygon_roundtrip(geoms in arb_mixed_polygon_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[ignore = \"encoder does not implement this correctly\"]\n        #[test]\n        fn test_cross_point_mls_roundtrip(geoms in arb_cross_point_mls_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[ignore = \"encoder does not implement this correctly\"]\n        #[test]\n        fn test_cross_point_mpoly_roundtrip(geoms in arb_cross_point_mpoly_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n\n        #[test]\n        fn test_cross_ls_mpoly_roundtrip(geoms in arb_cross_ls_mpoly_geoms()) {\n            let (canonical, output) = roundtrip_via_push(&geoms);\n            prop_assert_eq!(output, canonical);\n        }\n    }\n\n    /// Verifies that a Morton-encoded vertex dictionary is fully expanded inside `from_encoded`.\n    /// This ensures `GeometryValues` always holds flat `(x, y)` pairs.\n    #[test]\n    fn test_morton_vertex_dictionary_expansion() {\n        use integer_encoding::VarIntWriter as _;\n\n        // Morton vertex dictionary: 3 unique entries.\n        // Raw codes [0, 16, 32] -> delta-encoded as [0, 16, 16].\n        // The MortonDelta logical encoding means the decoder will undo the delta,\n        // then decode each Morton code to an (x, y) pair.\n        let mut raw_bytes = vec![];\n        let mut buf = [0u8; 10];\n        for &v in &[0_u64, 16, 16] {\n            let n = v.encode_var(&mut buf);\n            raw_bytes.extend_from_slice(&buf[..n]);\n        }\n\n        let morton_dict = EncodedStream {\n            meta: StreamMeta::new(\n                StreamType::Data(DictionaryType::Morton),\n                IntEncoding::new(\n                    LogicalEncoding::MortonDelta(Morton { bits: 3, shift: 0 }),\n                    PhysicalEncoding::VarInt,\n                ),\n                3, // 3 dictionary entries -> 3 physical u32 values\n            ),\n            data: raw_bytes,\n        };\n\n        // Assemble, serialize, parse, decode — same wire layout as geometry encoder:\n        // stream count, then meta (geom type), parts, vertex offsets, Morton dict.\n        let mut codecs = Codecs::default();\n        let mut enc = Encoder::with_explicit(\n            Encoder::default().cfg,\n            ExplicitEncoder::all(IntEncoder::varint()),\n        );\n        enc.write_varint(4u32).unwrap();\n        codecs\n            .write_int_stream(\n                &[GeometryType::LineString as u32],\n                &StreamCtx::geom(StreamType::Length(LengthType::VarBinary), \"meta\"),\n                &mut enc,\n            )\n            .unwrap();\n        codecs\n            .write_int_stream(\n                &[4u32],\n                &StreamCtx::geom(StreamType::Length(LengthType::Parts), \"parts\"),\n                &mut enc,\n            )\n            .unwrap();\n        codecs\n            .write_int_stream(\n                &[0u32, 1, 2, 1],\n                &StreamCtx::geom(StreamType::Offset(OffsetType::Vertex), \"vertex\"),\n                &mut enc,\n            )\n            .unwrap();\n        enc.write_stream(&morton_dict).unwrap();\n        let buffer = enc.data;\n\n        let mut p = parser();\n        let parsed = assert_empty(RawGeometry::from_bytes(&buffer, &mut p));\n        assert_snapshot!(p.reserved(), @\"72\");\n\n        let mut d = dec();\n        let decoded = LazyParsed::Raw(parsed).into_parsed(&mut d).unwrap();\n        assert_snapshot!(d.consumed(), @\"100\");\n        assert_eq!(decoded.vertices, Some(vec![0i32, 0, 4, 0, 0, 4, 4, 0]));\n\n        let geom = decoded.to_geojson(0).unwrap();\n        assert_eq!(geom, wkt!(LINESTRING(0 0,4 0,0 4,4 0)).into());\n    }\n\n    mod tessellation_tests {\n        use geo_types::{Geometry, LineString, MultiPolygon, Polygon};\n\n        use crate::decoder::GeometryValues;\n\n        #[test]\n        fn earcut_polygon_indices_in_range() {\n            let exterior = LineString::from(vec![(0_i32, 0), (10, 0), (10, 10), (0, 10), (0, 0)]);\n            let polygon = Polygon::new(exterior, vec![]);\n            let mut g = GeometryValues::new_tessellated();\n            g.push_geom(&Geometry::<i32>::Polygon(polygon));\n            let tris = g.triangles().expect(\"triangles\");\n            let n = tris[0];\n            assert!(n > 0, \"expected at least one triangle\");\n            let ib = g.index_buffer().expect(\"index buffer\");\n            assert_eq!(ib.len(), usize::try_from(n).unwrap() * 3);\n            // 4 unique (non-closing) vertices → indices in 0..4\n            assert!(ib.iter().all(|&i| i < 4));\n        }\n\n        #[test]\n        fn earcut_vertex_offset_for_multi_polygon_parts() {\n            let exterior1 = LineString::from(vec![(0_i32, 0), (10, 0), (10, 10), (0, 10), (0, 0)]);\n            let poly1 = Polygon::new(exterior1, vec![]);\n            let exterior2 = LineString::from(vec![(20, 0), (30, 0), (30, 10), (20, 10), (20, 0)]);\n            let poly2 = Polygon::new(exterior2, vec![]);\n            let mut g = GeometryValues::new_tessellated();\n            g.push_geom(&Geometry::<i32>::MultiPolygon(MultiPolygon(vec![\n                poly1, poly2,\n            ])));\n            let ib = g.index_buffer().expect(\"index buffer\");\n            let tris = g.triangles().expect(\"triangles\");\n            assert_eq!(tris.len(), 1);\n            let total = usize::try_from(tris[0]).unwrap();\n            assert_eq!(ib.len(), total * 3);\n            // First quad: 4 verts → 2 triangles, 6 indices\n            let split = 6;\n            let (first, second) = ib.split_at(split);\n            assert!(\n                first.iter().all(|&i| i < 4),\n                \"first polygon indices should reference verts 0..4: {first:?}\"\n            );\n            assert!(\n                second.iter().all(|&i| (4..8).contains(&i)),\n                \"second polygon indices should reference verts 4..8: {second:?}\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/geometry/mod.rs",
    "content": "pub(crate) mod encode;\nmod geotype;\nmod model;\n#[cfg(test)]\nmod tests;\n\npub use model::*;\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/geometry/model.rs",
    "content": "/// Describes how the vertex buffer should be encoded.\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub enum VertexBufferType {\n    /// Standard 2D `(x, y)` pairs encoded with componentwise delta.\n    #[default]\n    Vec2,\n    /// Morton (Z-order) dictionary encoding:\n    /// Unique vertices are sorted by their Morton code and stored once.\n    /// Each vertex position in the stream is replaced by its index into that dictionary.\n    Morton,\n    /// Hilbert curve dictionary encoding:\n    /// Unique vertices are sorted by their Hilbert curve index and stored once,\n    /// componentwise-delta encoded. Each vertex position in the stream is replaced\n    /// by its index into that dictionary.\n    Hilbert,\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/geometry/tests.rs",
    "content": "use std::collections::HashSet;\n\nuse geo_types::{Coord, Geometry, LineString, Point, Polygon, point, wkt};\nuse pretty_assertions::assert_eq;\nuse rstest::rstest;\n\nuse crate::decoder::RawGeometry;\nuse crate::encoder::model::EncoderConfig;\nuse crate::encoder::{Codecs, Encoder, ExplicitEncoder, IntEncoder, VertexBufferType};\nuse crate::test_helpers::{assert_empty, dec, parser};\nuse crate::{Decode as _, DictionaryType, GeometryValues, LengthType, StreamType};\n\n#[rstest]\n#[case::single_point(push_geoms(&[wkt!(POINT(10 20)).into()]))]\n#[case::linestring(push_geoms(&[wkt!(LINESTRING(10 20, 30 40, 50 60)).into()]))]\n#[case::polygon(push_geoms(&[wkt!(POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))).into()]))]\n#[case::multi_polygon(push_geoms(&[wkt!(MULTIPOLYGON(((0 0, 10 0, 10 10, 0 0),(5 5, 15 5, 15 15, 5 15)))).into()]))]\nfn automatic_optimization_roundtrip(#[case] decoded: GeometryValues) {\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    decoded\n        .clone()\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"optimize failed\");\n    assert_geometry_roundtrip(&enc.data, &decoded);\n}\n\nfn auto_mode_streams(decoded: &GeometryValues) -> Vec<StreamType> {\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    decoded\n        .clone()\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"encode failed\");\n\n    let mut stream_types: Vec<StreamType> = encoded_stream_types(&enc.data).into_iter().collect();\n    stream_types.sort();\n\n    assert_geometry_roundtrip(&enc.data, decoded);\n    stream_types\n}\n\n#[test]\nfn automatic_optimization_distinct_points_picks_vec2() {\n    let decoded = push_geoms(\n        &(0i32..10)\n            .map(|i| point! { x: i, y: i }.into())\n            .collect::<Vec<_>>(),\n    );\n    insta::assert_debug_snapshot!(auto_mode_streams(&decoded), @r\"\n    [\n        Data(\n            Vertex,\n        ),\n        Length(\n            VarBinary,\n        ),\n    ]\n    \");\n}\n\n#[test]\nfn automatic_optimization_repeated_points_picks_dict() {\n    // The Hilbert vs. Morton race resolves deterministically for this input —\n    // Hilbert wins, so the encoded streams use `Data(Vertex)` + a vertex\n    // offset stream. The snapshot pins that outcome; if the race tie-break\n    // or the heuristic ever changes it should fail loudly.\n    let decoded =\n        push_geoms(&std::iter::repeat_n(point! { x: 5, y: 5 }.into(), 20).collect::<Vec<_>>());\n    insta::assert_debug_snapshot!(auto_mode_streams(&decoded), @r\"\n    [\n        Data(\n            Vertex,\n        ),\n        Offset(\n            Vertex,\n        ),\n        Length(\n            VarBinary,\n        ),\n    ]\n    \");\n}\n\n#[test]\nfn encoded_output_always_has_meta_stream() {\n    let decoded = push_geoms(&[Geometry::<i32>::Point(Point(Coord::<i32> { x: 1, y: 1 }))]);\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    decoded\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"encode failed\");\n    let raw = assert_empty(RawGeometry::from_bytes(&enc.data, &mut parser()));\n\n    assert_eq!(\n        raw.meta.meta.stream_type,\n        StreamType::Length(LengthType::VarBinary),\n        \"meta (VarBinary) stream must always be present\"\n    );\n}\n\n#[test]\nfn encoded_polygon_has_topology_streams() {\n    let coords: Vec<Coord<i32>> = [(0, 0), (10, 0), (10, 10), (0, 0)]\n        .into_iter()\n        .map(|(x, y)| Coord::<i32> { x, y })\n        .collect();\n    let decoded = push_geoms(&[Geometry::<i32>::Polygon(Polygon::new(\n        LineString(coords),\n        vec![],\n    ))]);\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    decoded\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"encode failed\");\n\n    let stream_types = encoded_stream_types(&enc.data);\n    assert!(\n        stream_types.contains(&StreamType::Length(LengthType::Rings))\n            || stream_types.contains(&StreamType::Length(LengthType::Parts)),\n        \"polygon must produce at least a Parts or Rings length stream\"\n    );\n}\n\n/// Encode `decoded` with the vertex layout pinned to `strategy`, return the\n/// (sorted) stream types in the wire output, and assert the bytes round-trip\n/// back to the same `GeometryValues`.\nfn forced_vertex_strategy_streams(\n    decoded: &GeometryValues,\n    strategy: VertexBufferType,\n) -> Vec<StreamType> {\n    let explicit = ExplicitEncoder {\n        vertex_buffer_type: strategy,\n        ..ExplicitEncoder::all(IntEncoder::varint())\n    };\n    let mut enc = Encoder::with_explicit(EncoderConfig::default(), explicit);\n    let mut codecs = Codecs::default();\n    decoded\n        .clone()\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"encode failed\");\n\n    let mut stream_types: Vec<StreamType> = encoded_stream_types(&enc.data).into_iter().collect();\n    stream_types.sort();\n\n    assert_geometry_roundtrip(&enc.data, decoded);\n    stream_types\n}\n\n/// Multipoint with repeated coordinates so dict paths actually dedup.\nfn repeated_multipoint() -> GeometryValues {\n    let mut g = GeometryValues::default();\n    g.push_geom(&wkt!(MULTIPOINT(5 5, 10 10, 5 5, 10 10, 0 0, 5 5)).into());\n    g\n}\n\n#[test]\nfn forced_vec2_streams() {\n    let streams = forced_vertex_strategy_streams(&repeated_multipoint(), VertexBufferType::Vec2);\n    insta::assert_debug_snapshot!(streams, @r\"\n    [\n        Data(\n            Vertex,\n        ),\n        Length(\n            VarBinary,\n        ),\n        Length(\n            Geometries,\n        ),\n    ]\n    \");\n}\n\n#[test]\nfn forced_morton_streams() {\n    let streams = forced_vertex_strategy_streams(&repeated_multipoint(), VertexBufferType::Morton);\n    insta::assert_debug_snapshot!(streams, @r\"\n    [\n        Data(\n            Morton,\n        ),\n        Offset(\n            Vertex,\n        ),\n        Length(\n            VarBinary,\n        ),\n        Length(\n            Geometries,\n        ),\n    ]\n    \");\n}\n\n#[test]\nfn forced_hilbert_streams() {\n    let streams = forced_vertex_strategy_streams(&repeated_multipoint(), VertexBufferType::Hilbert);\n    insta::assert_debug_snapshot!(streams, @r\"\n    [\n        Data(\n            Vertex,\n        ),\n        Offset(\n            Vertex,\n        ),\n        Length(\n            VarBinary,\n        ),\n        Length(\n            Geometries,\n        ),\n    ]\n    \");\n}\n\n#[test]\nfn manual_encode_works() {\n    let decoded = push_geoms(&[wkt!(POINT(10 20)).into()]);\n\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    decoded\n        .clone()\n        .write_to(&mut enc, &mut codecs)\n        .expect(\"encode failed\");\n    let types = encoded_stream_types(&enc.data);\n    assert!(types.contains(&StreamType::Data(DictionaryType::Vertex)));\n\n    assert_geometry_roundtrip(&enc.data, &decoded);\n}\n\n/// Round-trip geometry bytes: parse then decode and compare.\nfn assert_geometry_roundtrip(data: &[u8], expected: &GeometryValues) {\n    let mut p = parser();\n    let mut d = dec();\n    let raw = assert_empty(RawGeometry::from_bytes(data, &mut p));\n    let result = raw.decode(&mut d).unwrap();\n    assert!(\n        d.consumed() > 0,\n        \"decoder should consume bytes after decode\"\n    );\n    assert_eq!(expected, &result);\n}\n\nfn push_geoms(geoms: &[Geometry<i32>]) -> GeometryValues {\n    let mut d = GeometryValues::default();\n    for g in geoms {\n        d.push_geom(g);\n    }\n    d\n}\n\n/// Collect all stream types present in the encoded geometry bytes (meta + items).\nfn encoded_stream_types(data: &[u8]) -> HashSet<StreamType> {\n    let raw = assert_empty(RawGeometry::from_bytes(data, &mut parser()));\n    std::iter::once(raw.meta.meta.stream_type)\n        .chain(raw.items.iter().map(|s| s.meta.stream_type))\n        .collect()\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/id/mod.rs",
    "content": "mod staged_id;\n\npub use staged_id::StagedId;\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/id/staged_id.rs",
    "content": "use crate::MltResult;\nuse crate::decoder::ColumnType;\nuse crate::encoder::optimizer::{Presence, PropertyStats};\nuse crate::encoder::{Codecs, Encoder, StagedOptScalar, StagedScalar};\n\n/// Staged ID column (encode-side, fully owned).\n///\n/// Mirrors the `StagedProperty` enum pattern but without a column name: the\n/// staged value itself carries the eventual ID column width and optionality.\n#[derive(Debug, Clone, PartialEq)]\npub enum StagedId {\n    None,\n    U32(StagedScalar<u32>),\n    OptU32(StagedOptScalar<u32>),\n    U64(StagedScalar<u64>),\n    OptU64(StagedOptScalar<u64>),\n}\n\nimpl StagedId {\n    /// Construct from a sparse `Vec<Option<u64>>`.\n    ///\n    /// Width is selected from the maximum present value; any `None` produces an\n    /// optional variant with a dense values vector.\n    #[must_use]\n    pub fn from_optional(ids: Vec<Option<u64>>) -> Self {\n        let len = ids.len();\n        let mut values = Vec::with_capacity(len);\n        let mut values64: Option<Vec<u64>> = None;\n        let mut presence: Option<Vec<bool>> = None;\n\n        for (idx, id) in ids.into_iter().enumerate() {\n            match id {\n                Some(id) => {\n                    if let Some(values64) = &mut values64 {\n                        values64.push(id);\n                    } else if let Ok(id) = u32::try_from(id) {\n                        values.push(id);\n                    } else {\n                        let mut promoted = Vec::with_capacity(len);\n                        promoted.extend(values.iter().copied().map(u64::from));\n                        promoted.push(id);\n                        values64 = Some(promoted);\n                    }\n                    if let Some(presence) = &mut presence {\n                        presence.push(true);\n                    }\n                }\n                None => {\n                    presence\n                        .get_or_insert_with(|| {\n                            let mut presence = Vec::with_capacity(len);\n                            presence.resize(idx, true);\n                            presence\n                        })\n                        .push(false);\n                }\n            }\n        }\n\n        if values.is_empty() && values64.is_none() {\n            Self::None\n        } else if let Some(presence) = presence {\n            if let Some(values64) = values64 {\n                Self::OptU64(StagedOptScalar::from_parts(\n                    String::new(),\n                    presence,\n                    values64,\n                ))\n            } else {\n                Self::OptU32(StagedOptScalar::from_parts(String::new(), presence, values))\n            }\n        } else if let Some(values64) = values64 {\n            Self::u64(values64)\n        } else {\n            Self::u32(values)\n        }\n    }\n\n    /// Construct from sparse IDs using a precomputed presence classification.\n    #[must_use]\n    pub(crate) fn from_optional_with_presence(\n        ids: impl IntoIterator<Item = Option<u64>>,\n        analysis: Option<&PropertyStats>,\n    ) -> Self {\n        let Some(analysis) = analysis else {\n            return Self::None;\n        };\n        match analysis.presence {\n            Presence::AllNull => Self::None,\n            Presence::AllPresent => {\n                Self::from_dense(ids.into_iter().flatten(), analysis.stats.values_fit_u32())\n            }\n            Presence::Mixed | Presence::SameAsProp(_) => {\n                Self::from_optional_sparse(ids, analysis.stats.values_fit_u32())\n            }\n        }\n    }\n\n    fn from_dense(ids: impl IntoIterator<Item = u64>, values_fit_u32: bool) -> Self {\n        if values_fit_u32 {\n            Self::u32(\n                ids.into_iter()\n                    .map(|id| {\n                        u32::try_from(id).expect(\"ID analysis guarantees u32-compatible values\")\n                    })\n                    .collect(),\n            )\n        } else {\n            Self::u64(ids.into_iter().collect())\n        }\n    }\n\n    fn from_optional_sparse(\n        ids: impl IntoIterator<Item = Option<u64>>,\n        values_fit_u32: bool,\n    ) -> Self {\n        let ids = ids.into_iter();\n        let (lower, upper) = ids.size_hint();\n        let capacity = upper.unwrap_or(lower);\n        let mut presence = Vec::with_capacity(capacity);\n        if values_fit_u32 {\n            let mut values = Vec::with_capacity(capacity);\n            for id in ids {\n                presence.push(id.is_some());\n                if let Some(id) = id {\n                    values.push(\n                        u32::try_from(id).expect(\"ID analysis guarantees u32-compatible values\"),\n                    );\n                }\n            }\n            Self::OptU32(StagedOptScalar::from_parts(String::new(), presence, values))\n        } else {\n            let mut values = Vec::with_capacity(capacity);\n            for id in ids {\n                presence.push(id.is_some());\n                if let Some(id) = id {\n                    values.push(id);\n                }\n            }\n            Self::OptU64(StagedOptScalar::from_parts(String::new(), presence, values))\n        }\n    }\n\n    #[must_use]\n    pub fn u32(values: Vec<u32>) -> Self {\n        Self::U32(StagedScalar {\n            name: String::new(),\n            values,\n        })\n    }\n\n    #[must_use]\n    pub fn opt_u32(values: impl IntoIterator<Item = Option<u32>>) -> Self {\n        Self::OptU32(StagedOptScalar::from_optional(String::new(), values))\n    }\n\n    #[must_use]\n    pub fn u64(values: Vec<u64>) -> Self {\n        Self::U64(StagedScalar {\n            name: String::new(),\n            values,\n        })\n    }\n\n    #[must_use]\n    pub fn opt_u64(values: impl IntoIterator<Item = Option<u64>>) -> Self {\n        Self::OptU64(StagedOptScalar::from_optional(String::new(), values))\n    }\n\n    /// Encode and write the ID column to `enc`.\n    #[hotpath::measure]\n    pub fn write_to(self, enc: &mut Encoder, codecs: &mut Codecs) -> MltResult<()> {\n        match &self {\n            Self::None => Ok(()),\n            Self::U32(v) => codecs.write_u32_scalar_col(ColumnType::Id, None, v, enc),\n            Self::OptU32(v) => codecs.write_opt_u32_scalar_col(ColumnType::OptId, None, v, enc),\n            Self::U64(v) => codecs.write_u64_scalar_col(ColumnType::LongId, None, v, enc),\n            Self::OptU64(v) => codecs.write_opt_u64_scalar_col(ColumnType::OptLongId, None, v, enc),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use geo_types::Point;\n    use proptest::prelude::*;\n    use rstest::rstest;\n\n    use crate::decoder::{ColumnType as CT, GeometryValues, RawId, RawIdValue};\n    use crate::encoder::stream::LogicalEncoder;\n    use crate::encoder::{\n        Codecs, Encoder, EncoderConfig, ExplicitEncoder, IntEncoder, StagedId, StagedLayer,\n    };\n    use crate::test_helpers::{dec, into_layer01, parser};\n    use crate::{Layer, LazyParsed, MltError, MltResult};\n\n    /// Round-trip `StagedId` via full layer bytes using an explicit encoder.\n    fn id_roundtrip_via_layer(decoded: &StagedId, int_enc: IntEncoder) -> StagedId {\n        if matches!(decoded, StagedId::None) {\n            return StagedId::from_optional(vec![]);\n        }\n        let n = feature_count(decoded);\n        let mut geometry = GeometryValues::default();\n        for _ in 0..n {\n            geometry.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n        }\n        let staged = StagedLayer {\n            name: \"id_roundtrip\".to_string(),\n            extent: 4096,\n            id: decoded.clone(),\n            geometry,\n            properties: vec![],\n        };\n        let enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::for_id(int_enc));\n        let mut codecs = Codecs::default();\n        let enc = staged.encode_into(enc, &mut codecs).expect(\"encode failed\");\n        let buf = enc.into_layer_bytes().expect(\"into_layer_bytes failed\");\n        let mut p = parser();\n        let (_, layer) = Layer::from_bytes(&buf, &mut p).expect(\"parse failed\");\n\n        let parsed = into_layer01(layer)\n            .id\n            .expect(\"expected id column\")\n            .into_parsed(&mut dec())\n            .expect(\"decode failed\");\n        StagedId::from_optional(parsed.materialize())\n    }\n\n    fn id_roundtrip_auto(decoded: &StagedId) -> StagedId {\n        if matches!(decoded, StagedId::None) {\n            return StagedId::from_optional(vec![]);\n        }\n        let n = feature_count(decoded);\n        let mut geometry = GeometryValues::default();\n        for _ in 0..n {\n            geometry.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n        }\n        let staged = StagedLayer {\n            name: \"id_roundtrip\".to_string(),\n            extent: 4096,\n            id: decoded.clone(),\n            geometry,\n            properties: vec![],\n        };\n        let mut codecs = Codecs::default();\n        let buf = staged\n            .encode_into(Encoder::default(), &mut codecs)\n            .expect(\"encode failed\")\n            .into_layer_bytes()\n            .expect(\"into_layer_bytes failed\");\n        let mut p = parser();\n        let mut d = dec();\n        let (_, layer) = Layer::from_bytes(&buf, &mut p).expect(\"parse failed\");\n        assert!(p.reserved() > 0, \"parser should reserve bytes after parse\");\n        let layer01 = into_layer01(layer);\n\n        let result = layer01\n            .id\n            .expect(\"expected id column\")\n            .into_parsed(&mut d)\n            .expect(\"decode failed\");\n        assert!(\n            d.consumed() > 0,\n            \"decoder should consume bytes after decode\"\n        );\n        StagedId::from_optional(result.materialize())\n    }\n\n    fn create_u32_range_ids() -> StagedId {\n        StagedId::from_optional((1u64..=100).map(Some).collect())\n    }\n\n    fn create_u64_range_ids() -> StagedId {\n        let base = u64::from(u32::MAX) + 1;\n        StagedId::from_optional((base..base + 50).map(Some).collect())\n    }\n\n    fn create_ids_with_nulls() -> StagedId {\n        StagedId::from_optional(vec![Some(10), None, Some(20), None, Some(30)])\n    }\n\n    fn create_constant_ids() -> StagedId {\n        StagedId::from_optional(vec![Some(42), Some(42), Some(42), Some(42), Some(42)])\n    }\n\n    fn feature_count(ids: &StagedId) -> usize {\n        match ids {\n            StagedId::None => 0,\n            StagedId::U32(v) => v.values.len(),\n            StagedId::OptU32(v) => v.presence.len(),\n            StagedId::U64(v) => v.values.len(),\n            StagedId::OptU64(v) => v.presence.len(),\n        }\n    }\n\n    fn id_values(ids: &StagedId) -> Vec<Option<u64>> {\n        match ids {\n            StagedId::None => Vec::new(),\n            StagedId::U32(v) => v.values.iter().copied().map(u64::from).map(Some).collect(),\n            StagedId::OptU32(v) => {\n                let mut dense = v.values.iter().copied().map(u64::from);\n                v.presence\n                    .iter()\n                    .map(|&p| if p { dense.next() } else { None })\n                    .collect()\n            }\n            StagedId::U64(v) => v.values.iter().copied().map(Some).collect(),\n            StagedId::OptU64(v) => {\n                let mut dense = v.values.iter().copied();\n                v.presence\n                    .iter()\n                    .map(|&p| if p { dense.next() } else { None })\n                    .collect()\n            }\n        }\n    }\n\n    /// Verify that automatic encoding produces no column for empty or all-null ID lists.\n    #[rstest]\n    #[case::empty(StagedId::from_optional(vec![]))]\n    #[case::all_nulls(StagedId::from_optional(vec![None, None]))]\n    fn test_automatic_encoding_skipped(#[case] input: StagedId) {\n        let mut enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        input.write_to(&mut enc, &mut codecs).unwrap();\n        assert_eq!(\n            enc.meta.len(),\n            0,\n            \"empty or all-null ID list should write no column\"\n        );\n    }\n\n    /// Verify that automatic encoding produces a column for non-trivial inputs.\n    #[rstest]\n    #[case::short_sequence(StagedId::from_optional(vec![Some(1), Some(2)]))]\n    #[case::sequential_u32(create_u32_range_ids())]\n    #[case::sequential_u64(create_u64_range_ids())]\n    #[case::constant(create_constant_ids())]\n    #[case::with_nulls(create_ids_with_nulls())]\n    fn test_automatic_encoding_produces_output(#[case] input: StagedId) {\n        let mut enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        input.write_to(&mut enc, &mut codecs).unwrap();\n        assert!(\n            !enc.meta.is_empty(),\n            \"non-trivial ID list should write a column\"\n        );\n    }\n\n    #[test]\n    fn test_automatic_optimization_roundtrip_empty() {\n        let decoded = StagedId::from_optional(vec![]);\n        let mut enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        decoded.write_to(&mut enc, &mut codecs).unwrap();\n        assert_eq!(enc.meta.len(), 0, \"empty ID list should write no column\");\n    }\n\n    #[rstest]\n    #[case::sequential_u32(create_u32_range_ids())]\n    #[case::sequential_u64(create_u64_range_ids())]\n    #[case::constant(create_constant_ids())]\n    #[case::with_nulls(create_ids_with_nulls())]\n    fn test_automatic_optimization_roundtrip(#[case] decoded: StagedId) {\n        let decoded_back = id_roundtrip_auto(&decoded);\n        assert_eq!(decoded_back, decoded);\n    }\n\n    #[test]\n    fn test_manual_optimization_applies_encoder() {\n        let decoded = StagedId::u64((1..=100).collect());\n        let decoded_back =\n            id_roundtrip_via_layer(&decoded, IntEncoder::varint_with(LogicalEncoder::None));\n        assert_eq!(id_values(&decoded_back), id_values(&decoded));\n    }\n\n    #[test]\n    fn test_manual_u32_roundtrip() {\n        let ids = StagedId::u32(vec![41]);\n        let decoded_back =\n            id_roundtrip_via_layer(&ids, IntEncoder::varint_with(LogicalEncoder::None));\n        assert_eq!(id_values(&decoded_back), vec![Some(41)]);\n    }\n\n    #[test]\n    fn test_manual_fastpfor_roundtrip() {\n        let ids = StagedId::u32((0u32..200).map(|i| i * 7 + 3).collect());\n        let decoded_back = id_roundtrip_via_layer(&ids, IntEncoder::fastpfor());\n        assert_eq!(decoded_back, ids);\n    }\n\n    /// Verify that large sequential u32 IDs produce a smaller encoding than plain varint\n    /// (confirming that delta+FastPFOR is being selected automatically).\n    #[test]\n    fn test_auto_fastpfor_beats_varint_for_large_u32_ids() {\n        let ids = StagedId::from_optional((0u64..1000).map(|i| Some(i * 13 + 5)).collect());\n\n        let mut auto_enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        ids.clone().write_to(&mut auto_enc, &mut codecs).unwrap();\n\n        let mut plain_enc = Encoder::with_explicit(\n            Encoder::default().cfg,\n            ExplicitEncoder::for_id(IntEncoder::varint()),\n        );\n        ids.write_to(&mut plain_enc, &mut codecs).unwrap();\n\n        assert!(\n            auto_enc.total_len() <= plain_enc.total_len(),\n            \"auto ({} bytes) should not be worse than plain varint ({} bytes)\",\n            auto_enc.total_len(),\n            plain_enc.total_len()\n        );\n    }\n\n    /// Verify that large u64 IDs round-trip correctly under automatic encoding.\n    #[test]\n    fn test_auto_roundtrip_large_u64_ids() {\n        let base = u64::from(u32::MAX) + 1;\n        let ids = StagedId::from_optional((0u64..1000).map(|i| Some(base + i * 13)).collect());\n        let decoded_back = id_roundtrip_auto(&ids);\n        assert_eq!(decoded_back, ids);\n    }\n\n    // Test that each config produces the correct variant and optional stream presence\n    #[rstest]\n    #[case::id32(CT::Id, StagedId::u32(vec![1, 2, 3]))]\n    #[case::empty_id32(CT::Id, StagedId::u32(vec![]))]\n    #[case::opt_id32(CT::OptId, StagedId::opt_u32(vec![Some(1), None, Some(3)]))]\n    #[case::id64(CT::LongId, StagedId::u64(vec![1, 2, 3]))]\n    #[case::opt_id64(CT::OptLongId, StagedId::opt_u64(vec![Some(1), None, Some(3)]))]\n    fn test_config_produces_correct_variant(#[case] column_type: CT, #[case] input: StagedId) {\n        let int_enc = IntEncoder::varint_with(LogicalEncoder::None);\n        with_encoded_raw_id(&input, int_enc, |raw_id| {\n            match column_type {\n                CT::OptId | CT::Id => assert!(matches!(raw_id.value, RawIdValue::Id32(_))),\n                CT::LongId | CT::OptLongId => assert!(matches!(raw_id.value, RawIdValue::Id64(_))),\n                _ => unreachable!(),\n            }\n            match column_type {\n                CT::OptId | CT::OptLongId => assert!(raw_id.presence.0.is_some()),\n                CT::Id | CT::LongId => assert!(raw_id.presence.0.is_none()),\n                _ => unreachable!(),\n            }\n        });\n    }\n\n    #[rstest]\n    #[case::id32_basic(StagedId::u32(vec![1, 2, 100, 1000]))]\n    #[case::id32_single(StagedId::u32(vec![42]))]\n    #[case::id32_boundaries(StagedId::u32(vec![0, u32::MAX]))]\n    #[case::id64_basic(StagedId::u64(vec![1, 2, 100, 1000]))]\n    #[case::id64_single(StagedId::u64(vec![u64::MAX]))]\n    #[case::id64_boundaries(StagedId::u64(vec![0, u64::MAX]))]\n    #[case::id64_large_values(StagedId::u64(vec![0, u64::from(u32::MAX), u64::from(u32::MAX) + 1, u64::MAX]))]\n    #[case::opt_id32_with_nulls(StagedId::opt_u32(vec![Some(1), None, Some(100), None, Some(1000)]))]\n    #[case::opt_id32_no_nulls(StagedId::opt_u32(vec![Some(1), Some(2), Some(3)]))]\n    #[case::opt_id32_single_null(StagedId::opt_u32(vec![None]))]\n    #[case::opt_id64_with_nulls(StagedId::opt_u64(vec![Some(1), None, Some(u64::from(u32::MAX) + 1), None, Some(u64::MAX)]))]\n    #[case::opt_id64_all_nulls(StagedId::opt_u64(vec![None, None, None]))]\n    #[case::none(StagedId::None)]\n    #[case::empty_id32(StagedId::u32(vec![]))]\n    fn test_roundtrip(#[case] ids: StagedId) {\n        let int_enc = IntEncoder::varint_with(LogicalEncoder::None);\n        assert_roundtrip(&ids, int_enc);\n    }\n\n    #[rstest]\n    fn test_sequential_ids(\n        #[values(LogicalEncoder::None)] logical: LogicalEncoder,\n        #[values(CT::Id, CT::OptId, CT::LongId, CT::OptLongId)] column_type: CT,\n    ) {\n        let input = match column_type {\n            CT::Id => StagedId::u32((1..=100).collect()),\n            CT::OptId => StagedId::opt_u32((1..=100).map(Some)),\n            CT::LongId => StagedId::u64((1..=100).collect()),\n            CT::OptLongId => StagedId::opt_u64((1..=100).map(Some)),\n            _ => unreachable!(),\n        };\n        let int_enc = IntEncoder::varint_with(logical);\n        assert_roundtrip(&input, int_enc);\n    }\n\n    proptest! {\n        #[test]\n        fn test_roundtrip_opt_id32(\n            ids in prop::collection::vec(prop::option::of(any::<u32>()), 1..100),\n            logical in any::<LogicalEncoder>()\n        ) {\n            prop_assert_roundtrip(&StagedId::opt_u32(ids), IntEncoder::varint_with(logical))?;\n        }\n\n        #[test]\n        fn test_roundtrip_id64(\n            ids in prop::collection::vec(any::<u64>(), 1..100),\n            logical in any::<LogicalEncoder>()\n        ) {\n            prop_assert_roundtrip(&StagedId::u64(ids), IntEncoder::varint_with(logical))?;\n        }\n\n        #[test]\n        fn test_roundtrip_id32(\n            ids in prop::collection::vec(any::<u32>(), 1..100),\n            logical in any::<LogicalEncoder>()\n        ) {\n            prop_assert_roundtrip(&StagedId::u32(ids), IntEncoder::varint_with(logical))?;\n        }\n\n        #[test]\n        fn test_roundtrip_opt_id64(\n            ids in prop::collection::vec(prop::option::of(any::<u64>()), 1..100),\n            logical in any::<LogicalEncoder>()\n        ) {\n            prop_assert_roundtrip(&StagedId::opt_u64(ids), IntEncoder::varint_with(logical))?;\n        }\n\n        #[test]\n        fn test_correct_variant_produced_id32(\n            ids in prop::collection::vec(1u32..1000u32, 1..50),\n            logical in any::<LogicalEncoder>()\n        ) {\n            assert_produces_correct_variant(&StagedId::u32(ids), CT::Id, IntEncoder::varint_with(logical))?;\n        }\n\n        #[test]\n        fn test_correct_variant_produced_id64(\n            ids in prop::collection::vec(any::<u64>(), 1..50),\n            logical in any::<LogicalEncoder>()\n        ) {\n            assert_produces_correct_variant(&StagedId::u64(ids), CT::LongId, IntEncoder::varint_with(logical))?;\n        }\n    }\n\n    /// Round-trip `StagedId` via full layer bytes (encode → bytes → parse → decode).\n    fn assert_roundtrip(ids: &StagedId, int_enc: IntEncoder) {\n        prop_assert_roundtrip(ids, int_enc).expect(\"roundtrip failed\");\n    }\n\n    fn prop_assert_roundtrip(ids: &StagedId, int_enc: IntEncoder) -> Result<(), TestCaseError> {\n        let res = roundtrip_id_values(ids, int_enc)\n            .map_err(|e| TestCaseError::Fail(format!(\"Roundtrip failed: {e:?}\").into()))?;\n        let expected = StagedId::from_optional(id_values(ids));\n        prop_assert_eq!(id_values(&res), id_values(&expected));\n        Ok(())\n    }\n\n    fn roundtrip_id_values(decoded: &StagedId, int_enc: IntEncoder) -> MltResult<StagedId> {\n        if matches!(decoded, StagedId::None) {\n            return Ok(StagedId::from_optional(vec![]));\n        }\n        let n = feature_count(decoded);\n        let mut geometry = GeometryValues::default();\n        for _ in 0..n {\n            geometry.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n        }\n        let staged = StagedLayer {\n            name: \"id_roundtrip\".to_string(),\n            extent: 4096,\n            id: decoded.clone(),\n            geometry,\n            properties: vec![],\n        };\n        let enc =\n            Encoder::with_explicit(EncoderConfig::default(), ExplicitEncoder::for_id(int_enc));\n        let mut codecs = Codecs::default();\n        let enc = staged.encode_into(enc, &mut codecs)?;\n        let buf = enc.into_layer_bytes()?;\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser())?;\n        let Layer::Tag01(layer01) = layer else {\n            return Err(MltError::NotDecoded(\"expected Tag01 layer\"));\n        };\n        // When all source IDs were null, the encoder skips the ID column entirely.\n        // On decode, the absent column is semantically identical to all-null IDs.\n        match layer01.id {\n            Some(id) => {\n                let parsed = id.into_parsed(&mut dec())?;\n                Ok(StagedId::from_optional(parsed.materialize()))\n            }\n            None => Ok(StagedId::from_optional(vec![None; n])),\n        }\n    }\n\n    /// Encode `ids` into a full layer, parse the raw ID, and pass it to `f`.\n    fn with_encoded_raw_id<R>(\n        ids: &StagedId,\n        int_enc: IntEncoder,\n        f: impl FnOnce(&RawId<'_>) -> R,\n    ) -> R {\n        // Encode a full StagedLayer, parse the layer back out, and inspect the raw ID field.\n        // This exercises the ID encoding as it appears in a real layer payload.\n        let n = feature_count(ids);\n        let mut geometry = GeometryValues::default();\n        for _ in 0..n {\n            geometry.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n        }\n        let staged = StagedLayer {\n            name: \"id_test\".to_string(),\n            extent: 4096,\n            id: ids.clone(),\n            geometry,\n            properties: vec![],\n        };\n        let enc =\n            Encoder::with_explicit(EncoderConfig::default(), ExplicitEncoder::for_id(int_enc));\n        let mut codecs = Codecs::default();\n        let enc = staged.encode_into(enc, &mut codecs).expect(\"encode failed\");\n        let buf = enc.into_layer_bytes().expect(\"into_layer_bytes failed\");\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).expect(\"parse failed\");\n        let Layer::Tag01(layer01) = layer else {\n            panic!(\"expected Tag01\")\n        };\n        let Some(LazyParsed::Raw(raw_id)) = layer01.id else {\n            panic!(\"expected raw id\")\n        };\n        f(&raw_id)\n    }\n\n    fn assert_produces_correct_variant(\n        input: &StagedId,\n        column_type: CT,\n        int_enc: IntEncoder,\n    ) -> Result<(), TestCaseError> {\n        with_encoded_raw_id(input, int_enc, |raw_id| {\n            if matches!(column_type, CT::Id | CT::OptId) {\n                prop_assert!(\n                    matches!(raw_id.value, RawIdValue::Id32(_)),\n                    \"Expected Id32 variant\"\n                );\n            } else {\n                prop_assert!(\n                    matches!(raw_id.value, RawIdValue::Id64(_)),\n                    \"Expected Id64 variant\"\n                );\n            }\n\n            if matches!(column_type, CT::OptId | CT::OptLongId) {\n                prop_assert!(\n                    raw_id.presence.0.is_some(),\n                    \"Expected optional stream to be present\"\n                );\n            } else {\n                prop_assert!(raw_id.presence.0.is_none(), \"Expected no optional stream\");\n            }\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/mod.rs",
    "content": "mod analyze;\n#[cfg(all(not(test), feature = \"arbitrary\"))]\nmod fuzzing;\nmod geometry;\nmod id;\npub(crate) mod model;\nmod optimizer;\nmod property;\nmod sort;\nmod stream;\n#[cfg(any(test, feature = \"__private\"))]\nmod tests;\nmod tile;\nmod unknown;\nmod writer;\n\n#[cfg(not(feature = \"__private\"))]\npub(crate) use geometry::VertexBufferType;\n#[cfg(feature = \"__private\")]\npub use geometry::VertexBufferType;\npub use id::StagedId;\n#[cfg(feature = \"__private\")]\npub use model::{ColumnKind, CurveParams, ExplicitEncoder, StagedLayer, StrEncoding, StreamCtx};\npub use model::{EncodedUnknown, EncoderConfig};\n#[cfg(all(test, not(feature = \"__private\")))]\npub(crate) use model::{ExplicitEncoder, StagedLayer, StrEncoding};\n#[cfg(any(test, feature = \"__private\"))]\npub use optimizer::Presence;\npub(crate) use property::*;\n#[cfg(feature = \"__private\")]\npub use property::{StagedProperty, StagedSharedDict};\npub use sort::SortStrategy;\npub(crate) use sort::spatial_sort_likely_to_help;\npub(crate) use stream::*;\n#[cfg(feature = \"__private\")]\npub use stream::{Codecs, IntEncoder, LogicalEncoder, PhysicalEncoder};\n#[cfg(any(test, feature = \"__private\"))]\npub use tests::stage_tile;\npub use writer::Encoder;\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/model.rs",
    "content": "use derive_debug::Dbg;\n\nuse crate::decoder::{DictionaryType, GeometryValues, StreamType};\nuse crate::encoder::geometry::VertexBufferType;\nuse crate::encoder::{IntEncoder, StagedId, StagedProperty};\n\n/// Owned variant of `Unknown`.\n#[derive(Debug, Clone, Default, PartialEq)]\npub struct EncodedUnknown {\n    pub(crate) tag: u8,\n    pub(crate) value: Vec<u8>,\n}\n\n/// Parameters derived from the vertex set of a feature collection, used to\n/// normalize coordinates before space-filling-curve key computation.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct CurveParams {\n    pub shift: u32,\n    pub bits: u32,\n}\n\nimpl Default for CurveParams {\n    fn default() -> Self {\n        Self { shift: 0, bits: 1 }\n    }\n}\n\nimpl CurveParams {\n    /// Compute params from a flat `[x0, y0, x1, y1, …]` vertex slice.\n    #[must_use]\n    pub fn from_vertices(vertices: &[i32]) -> Self {\n        if vertices.is_empty() {\n            return Self::default();\n        }\n        let (min, max) = vertices\n            .iter()\n            .fold((i32::MAX, i32::MIN), |(mn, mx), &v| (mn.min(v), mx.max(v)));\n        crate::codecs::hilbert::hilbert_curve_params_from_bounds(min, max)\n    }\n}\n\n/// Columnar layer data being prepared for encoding (stage 2 of the encoding pipeline).\n///\n/// Holds fully-owned columnar data. Constructed directly (synthetics, benches) or\n/// converted from [`TileLayer`](crate::TileLayer).\n/// Consumed by encoding via [`StagedLayer::encode_into`] or `StagedLayer::encode_explicit`\n/// (with explicit encoding mode enabled).\n#[derive(Debug, PartialEq, Clone)]\npub struct StagedLayer {\n    pub name: String,\n    pub extent: u32,\n    pub id: StagedId,\n    pub geometry: GeometryValues,\n    pub properties: Vec<StagedProperty>,\n}\n\n/// Global encoder settings controlling which optimization strategies are attempted.\n#[derive(Debug, Clone, Copy, PartialEq, Hash)]\n#[expect(\n    clippy::struct_excessive_bools,\n    reason = \"enums would not model this better, not a state machine\"\n)]\npub struct EncoderConfig {\n    /// Generate tessellation data for polygons and multi-polygons.\n    pub tessellate: bool,\n    /// Try sorting features by the Z-order (Morton) curve index of their first vertex.\n    pub try_spatial_morton_sort: bool,\n    /// Try sorting features by the Hilbert curve index of their first vertex.\n    pub try_spatial_hilbert_sort: bool,\n    /// Try sorting features by their feature ID in ascending order.\n    pub try_id_sort: bool,\n    /// Allow `FSST` string compression\n    pub allow_fsst: bool,\n    /// Allow `FastPFOR` integer compression\n    pub allow_fpf: bool,\n    /// Allow string grouping into shared dictionaries\n    pub allow_shared_dict: bool,\n}\nimpl Default for EncoderConfig {\n    fn default() -> Self {\n        Self {\n            tessellate: false,\n            try_spatial_morton_sort: true,\n            try_spatial_hilbert_sort: true,\n            try_id_sort: true,\n            allow_fsst: true,\n            allow_fpf: true,\n            allow_shared_dict: true,\n        }\n    }\n}\n\n/// How to encode a string column.\n///\n/// Used by [`ExplicitEncoder`] to control per-column string encoding in the\n/// explicit (synthetics / `__private`) path and in property-encoding helpers.\n///\n/// Publicly visible only when the `__private` feature is enabled (re-exported from\n/// [`crate::encoder`]).  Always compiled so that the unified property-encoding path\n/// can reference it without feature flags.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum StrEncoding {\n    Plain,\n    Dict,\n    Fsst,\n    FsstDict,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum ColumnKind {\n    Id,\n    Geometry,\n    Property,\n}\n\n/// Context for per-stream encoding decisions in [`ExplicitEncoder`] callbacks.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub struct StreamCtx<'a> {\n    pub kind: ColumnKind,\n    pub stream_type: StreamType,\n    pub name: &'a str,\n    pub subname: &'a str,\n}\nimpl<'a> StreamCtx<'a> {\n    /// Stream with a logical sub-part (e.g. string column `\"lengths\"` / `\"offsets\"`, shared-dict child suffix).\n    #[inline]\n    #[must_use]\n    pub const fn new(\n        kind: ColumnKind,\n        stream_type: StreamType,\n        name: &'a str,\n        subname: &'a str,\n    ) -> Self {\n        Self {\n            kind,\n            stream_type,\n            name,\n            subname,\n        }\n    }\n\n    #[inline]\n    #[must_use]\n    pub const fn id(stream_type: StreamType) -> Self {\n        Self::new(ColumnKind::Id, stream_type, \"\", \"\")\n    }\n\n    #[inline]\n    #[must_use]\n    pub const fn geom(stream_type: StreamType, name: &'a str) -> Self {\n        Self::new(ColumnKind::Geometry, stream_type, name, \"\")\n    }\n\n    #[inline]\n    #[must_use]\n    pub const fn prop(stream_type: StreamType, name: &'a str) -> Self {\n        Self::new(ColumnKind::Property, stream_type, name, \"\")\n    }\n\n    #[inline]\n    #[must_use]\n    pub const fn prop_data(name: &'a str) -> Self {\n        let stream_type = StreamType::Data(DictionaryType::None);\n        Self::new(ColumnKind::Property, stream_type, name, \"\")\n    }\n\n    #[inline]\n    #[must_use]\n    pub const fn prop2(stream_type: StreamType, prefix: &'a str, suffix: &'a str) -> Self {\n        Self::new(ColumnKind::Property, stream_type, prefix, suffix)\n    }\n}\n\n/// Explicit, deterministic encoding configuration for synthetics and tests.\n///\n/// All encoding choices are caller-specified via callbacks so one struct can cover\n/// any combination without per-stream boilerplate.\n///\n/// Always compiled; publicly visible only when the `__private` feature is enabled\n/// (re-exported from [`crate::encoder`]).\n#[derive(Dbg)]\npub struct ExplicitEncoder {\n    /// Vertex buffer layout for geometry streams.\n    pub vertex_buffer_type: VertexBufferType,\n    /// Per-stream override for the skip-empty-stream rule used by `write_geo_u32_stream`.\n    #[dbg(skip)]\n    pub force_stream: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> bool>,\n    /// Return the [`IntEncoder`] for a stream identified by [`StreamCtx`].\n    #[dbg(skip)]\n    pub get_int_encoder: Box<dyn for<'a> Fn(&'a StreamCtx<'a>) -> IntEncoder>,\n    /// Return the string encoding strategy for a string property column.\n    #[dbg(skip)]\n    pub get_str_encoding: Box<dyn Fn(&str) -> StrEncoding>,\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/optimizer.rs",
    "content": "use bitvec::vec::BitVec;\n\nuse crate::decoder::{Morton, PropKind, TileLayer};\nuse crate::encoder::model::{CurveParams, StagedLayer};\nuse crate::encoder::property::encode::write_properties;\nuse crate::encoder::{\n    Codecs, Encoder, EncoderConfig, SortStrategy, StagedId, spatial_sort_likely_to_help,\n};\nuse crate::{MltError, MltResult, PropValue};\n\nimpl StagedLayer {\n    /// Encode and serialize the layer directly into `enc`, without creating any\n    /// intermediate representation.\n    ///\n    /// This is the hot path inside `TileLayer::encode`: each sort-strategy\n    /// trial calls this method on its own fresh `Encoder`, and only the\n    /// `Encoder` with the smallest `total_len()` is kept.\n    #[hotpath::measure]\n    pub fn encode_into(self, mut enc: Encoder, codecs: &mut Codecs) -> MltResult<Encoder> {\n        let column_count = usize::from(!matches!(&self.id, StagedId::None))\n            + 1 // geometry\n            + self.properties.len();\n\n        let Self {\n            name,\n            extent,\n            id,\n            geometry,\n            properties,\n        } = self;\n\n        id.write_to(&mut enc, codecs)?;\n        geometry.write_to(&mut enc, codecs)?;\n        write_properties(&properties, &mut enc, codecs)?;\n        enc.write_header(&name, extent, column_count)?;\n\n        Ok(enc)\n    }\n}\n\n/// Seed the encoder's curve-derived caches so the Hilbert/Morton dictionary\n/// builders skip their min/max scan. `Morton::new` returns `Err` when bits > 16;\n/// `dict_may_be_beneficial` reads `morton_cache.is_none()` and falls back to\n/// a Vec2-only path in that case.\nfn seed_curve_caches(enc: &mut Encoder, curve_params: CurveParams) {\n    enc.hilbert_cache = Some(curve_params);\n    enc.morton_cache = Morton::new(curve_params.bits, curve_params.shift).ok();\n}\n\n/// Feature-count threshold above which the spatial trial is subject to the\n/// bounding-box pruning heuristic.\nconst SORT_TRIAL_THRESHOLD: usize = 512;\n\nimpl TileLayer {\n    /// Encode a [`TileLayer`] to bytes, automatically optimizing all encoding choices.\n    ///\n    /// This is the primary encoding entry point. It:\n    /// 1. Determines which sort strategies to try based on `cfg`\n    /// 2. Tries each sort strategy, encoding and measuring the output size\n    /// 3. Returns the smallest encoding as a complete layer record (including tag and length prefix)\n    ///\n    /// All encoding choices — sort order, per-stream integer encodings, string compression,\n    /// vertex buffer layout — are selected automatically to minimize output size.\n    #[hotpath::measure]\n    pub fn encode(self, cfg: EncoderConfig) -> MltResult<Vec<u8>> {\n        if self.features.is_empty() {\n            return Ok(Vec::new());\n        }\n\n        let mut sort_by = vec![SortStrategy::Unsorted];\n        let try_spatial_sort = cfg.try_spatial_morton_sort || cfg.try_spatial_hilbert_sort;\n        if try_spatial_sort\n            && (self.features.len() < SORT_TRIAL_THRESHOLD || spatial_sort_likely_to_help(&self))\n        {\n            if cfg.try_spatial_morton_sort {\n                sort_by.push(SortStrategy::SpatialMorton);\n            }\n            if cfg.try_spatial_hilbert_sort {\n                sort_by.push(SortStrategy::SpatialHilbert);\n            }\n        }\n        if cfg.try_id_sort {\n            sort_by.push(SortStrategy::Id);\n        }\n\n        let stats = self.analyze(cfg.allow_shared_dict)?;\n        // Bounds are order-invariant, so this scan is shared across every\n        // sort trial and the encoder's Hilbert/Morton dictionary builders.\n        let curve_params = self.curve_params();\n\n        // `Encoder::preserve_results` clears caches only on the moved-out\n        // archive, so a single seeding here serves every trial that reuses\n        // `enc`.\n        let mut enc = Encoder::new(cfg);\n        seed_curve_caches(&mut enc, curve_params);\n\n        let (last, init) = sort_by.split_last().expect(\"at least one strategy\");\n        if init.is_empty() {\n            let mut codecs = Codecs::default();\n            StagedLayer::from_tile(self, *last, &stats, cfg.tessellate, curve_params)\n                .encode_into(enc, &mut codecs)?\n        } else {\n            let mut codecs = Codecs::default();\n            enc = {\n                let first = init[0];\n                StagedLayer::from_tile(self.clone(), first, &stats, cfg.tessellate, curve_params)\n                    .encode_into(enc, &mut codecs)?\n            };\n            let mut best = enc.preserve_results();\n            // Clone for all-but-last strategies\n            for &sort in &init[1..] {\n                let layer = StagedLayer::from_tile(\n                    self.clone(),\n                    sort,\n                    &stats,\n                    cfg.tessellate,\n                    curve_params,\n                );\n                enc = layer.encode_into(enc, &mut codecs)?;\n                if enc.total_len() < best.total_len() {\n                    best = enc.preserve_results();\n                }\n            }\n            // Last strategy: consume self, no clone\n            let layer = StagedLayer::from_tile(self, *last, &stats, cfg.tessellate, curve_params);\n            enc = layer.encode_into(enc, &mut codecs)?;\n            if enc.total_len() < best.total_len() {\n                best = enc.preserve_results();\n            }\n            best\n        }\n        .into_layer_bytes()\n    }\n}\n\n/// Row-order-independent presence classification for IDs and properties.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Presence {\n    /// No feature has a value for this logical column.\n    AllNull,\n    /// Every feature has a value for this logical column.\n    AllPresent,\n    /// Some, but not all, features have a value for this logical column.\n    Mixed,\n    /// Mixed presence with the same per-feature mask as an earlier property column.\n    SameAsProp(usize),\n}\nimpl Presence {\n    /// Create presence value\n    #[must_use]\n    pub fn from_bits(bits: &BitVec<u8>, existing: &[(BitVec<u8>, usize)]) -> Self {\n        if bits.not_any() {\n            Self::AllNull\n        } else if bits.all() {\n            Self::AllPresent\n        } else if let Some((_, idx)) = existing.iter().find(|(v, _)| v == bits) {\n            Self::SameAsProp(*idx)\n        } else {\n            Self::Mixed\n        }\n    }\n}\n\n/// How a property participates in a shared dictionary group.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum SharedDictRole {\n    /// The property is encoded as a standalone column.\n    None,\n    /// The property is the first column in this group and emits the shared dictionary with this prefix.\n    Owner(String),\n    /// The property is emitted by the group owner at this property index.\n    Member(usize),\n}\n\n/// Row-order-independent facts for a single property column.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct PropertyStats {\n    pub presence: Presence,\n    pub stats: PropertyTypedStats,\n}\n\n/// Row-order-independent layer facts computed once before sort trials.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LayerStats {\n    pub id: Option<PropertyStats>,\n    pub properties: Vec<PropertyStats>,\n}\n\n/// Row-order-independent value statistics for a property column.\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\npub enum PropertyTypedStats {\n    /// No present values.\n    #[default]\n    None,\n    Bool,\n    Signed {\n        min: i64,\n        max: i64,\n    },\n    Unsigned {\n        min: u64,\n        max: u64,\n    },\n    F32,\n    F64,\n    String {\n        shared_dict: SharedDictRole,\n    },\n}\n\nimpl PropertyTypedStats {\n    #[must_use]\n    pub fn values_fit_u32(&self) -> bool {\n        match self {\n            Self::None | Self::Bool | Self::F32 | Self::F64 | Self::String { .. } => false,\n            Self::Signed { min, max } => *min >= 0 && u32::try_from(*max).is_ok(),\n            Self::Unsigned { max, .. } => u32::try_from(*max).is_ok(),\n        }\n    }\n\n    #[must_use]\n    pub fn shared_dict(&self) -> SharedDictRole {\n        match self {\n            Self::String { shared_dict, .. } => shared_dict.clone(),\n            _ => SharedDictRole::None,\n        }\n    }\n\n    pub(crate) fn set_shared_dict(&mut self, role: SharedDictRole) {\n        match self {\n            Self::String { shared_dict, .. } => *shared_dict = role,\n            _ => debug_assert_eq!(role, SharedDictRole::None),\n        }\n    }\n\n    pub(crate) fn push(\n        &mut self,\n        prop: &PropValue,\n        column_idx: usize,\n        property_name: &str,\n    ) -> MltResult<bool> {\n        match prop {\n            PropValue::Bool(Some(_)) => {\n                self.merge_same_kind(Self::Bool, column_idx, property_name)?;\n            }\n            PropValue::I8(Some(v)) => {\n                self.merge_signed(i64::from(*v), column_idx, property_name)?;\n            }\n            PropValue::U8(Some(v)) => {\n                self.merge_unsigned(u64::from(*v), column_idx, property_name)?;\n            }\n            PropValue::I32(Some(v)) => {\n                self.merge_signed(i64::from(*v), column_idx, property_name)?;\n            }\n            PropValue::U32(Some(v)) => {\n                self.merge_unsigned(u64::from(*v), column_idx, property_name)?;\n            }\n            PropValue::I64(Some(v)) => self.merge_signed(*v, column_idx, property_name)?,\n            PropValue::U64(Some(v)) => self.merge_unsigned(*v, column_idx, property_name)?,\n            PropValue::F32(Some(_)) => {\n                self.merge_same_kind(Self::F32, column_idx, property_name)?;\n            }\n            PropValue::F64(Some(_)) => {\n                self.merge_same_kind(Self::F64, column_idx, property_name)?;\n            }\n            PropValue::Str(Some(_)) => self.merge_string(column_idx, property_name)?,\n            _ => return Ok(false),\n        }\n        Ok(true)\n    }\n\n    fn merge_signed(\n        &mut self,\n        value: i64,\n        column_idx: usize,\n        property_name: &str,\n    ) -> MltResult<()> {\n        match self {\n            Self::None => {\n                *self = Self::Signed {\n                    min: value,\n                    max: value,\n                };\n            }\n            Self::Signed { min, max } => {\n                *min = (*min).min(value);\n                *max = (*max).max(value);\n            }\n            _ => return mixed_prop_err(column_idx, property_name),\n        }\n        Ok(())\n    }\n\n    fn merge_unsigned(\n        &mut self,\n        value: u64,\n        column_idx: usize,\n        property_name: &str,\n    ) -> MltResult<()> {\n        match self {\n            Self::None => {\n                *self = Self::Unsigned {\n                    min: value,\n                    max: value,\n                };\n            }\n            Self::Unsigned { min, max } => {\n                *min = (*min).min(value);\n                *max = (*max).max(value);\n            }\n            _ => return mixed_prop_err(column_idx, property_name),\n        }\n        Ok(())\n    }\n\n    fn merge_string(&mut self, column_idx: usize, property_name: &str) -> MltResult<()> {\n        match self {\n            Self::None => {\n                *self = Self::String {\n                    shared_dict: SharedDictRole::None,\n                };\n            }\n            Self::String { .. } => {}\n            _ => return mixed_prop_err(column_idx, property_name),\n        }\n        Ok(())\n    }\n\n    fn merge_same_kind(\n        &mut self,\n        kind: Self,\n        column_idx: usize,\n        property_name: &str,\n    ) -> MltResult<()> {\n        match self {\n            Self::None => *self = kind,\n            Self::Bool if matches!(kind, Self::Bool) => {}\n            Self::F32 if matches!(kind, Self::F32) => {}\n            Self::F64 if matches!(kind, Self::F64) => {}\n            _ => return mixed_prop_err(column_idx, property_name),\n        }\n        Ok(())\n    }\n}\n\nimpl TileLayer {\n    /// Analyze a [`TileLayer`] and return reusable ID/property facts for the optimizer.\n    #[hotpath::measure]\n    pub(crate) fn analyze(&self, allow_shared_dict: bool) -> MltResult<LayerStats> {\n        let mut property_bits = Vec::with_capacity(self.property_names.len());\n        let mut properties = self.analyze_properties(&mut property_bits)?;\n        let id = self.analyze_ids(&property_bits);\n        if allow_shared_dict {\n            self.group_string_properties(&mut properties);\n        }\n        Ok(LayerStats { id, properties })\n    }\n\n    fn analyze_ids(&self, property_bits: &[(BitVec<u8>, usize)]) -> Option<PropertyStats> {\n        let mut min = u64::MAX;\n        let mut max = 0u64;\n        let mut bits = BitVec::<u8>::with_capacity(self.features.len());\n        for feature in &self.features {\n            if let Some(id) = feature.id {\n                min = min.min(id);\n                max = max.max(id);\n                bits.push(true);\n            } else {\n                bits.push(false);\n            }\n        }\n        let presence = Presence::from_bits(&bits, property_bits);\n        if presence == Presence::AllNull {\n            None\n        } else {\n            Some(PropertyStats {\n                presence,\n                stats: PropertyTypedStats::Unsigned { min, max },\n            })\n        }\n    }\n\n    fn analyze_properties(\n        &self,\n        property_bits: &mut Vec<(BitVec<u8>, usize)>,\n    ) -> MltResult<Vec<PropertyStats>> {\n        self.property_names\n            .iter()\n            .enumerate()\n            .map(|(col_idx, name)| -> MltResult<PropertyStats> {\n                let mut kind = None;\n                let mut stats = PropertyTypedStats::default();\n                let mut bits = BitVec::<u8>::with_capacity(self.features.len());\n                for feature in &self.features {\n                    let prop = feature.properties.get(col_idx);\n                    if let Some(prop_kind) = prop.map(PropKind::from) {\n                        match kind {\n                            Some(kind) if kind != prop_kind => {\n                                return mixed_prop_err(col_idx, name.as_str());\n                            }\n                            None => kind = Some(prop_kind),\n                            _ => {}\n                        }\n                    }\n                    if let Some(prop) = prop\n                        && stats.push(prop, col_idx, name)?\n                    {\n                        bits.push(true);\n                    } else {\n                        bits.push(false);\n                    }\n                }\n\n                let presence = Presence::from_bits(&bits, property_bits);\n                if presence == Presence::Mixed {\n                    property_bits.push((bits, col_idx));\n                }\n                Ok(PropertyStats { presence, stats })\n            })\n            .collect()\n    }\n}\n\n#[inline]\nfn mixed_prop_err<T>(column_idx: usize, property_name: &str) -> MltResult<T> {\n    Err(MltError::MixedPropertyTypes(\n        column_idx,\n        property_name.to_owned(),\n    ))\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/encode.rs",
    "content": "use super::model::{StagedOptScalar, StagedProperty};\nuse crate::MltResult;\nuse crate::decoder::{ColumnType, DictionaryType, StreamType};\nuse crate::encoder::model::StreamCtx;\nuse crate::encoder::{Codecs, Encoder, StagedScalar, StagedStrings};\n\n/// Encode all property columns and write them to `enc`.\n#[hotpath::measure]\npub fn write_properties(\n    props: &[StagedProperty],\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    for prop in props {\n        write_prop(prop, enc, codecs)?;\n    }\n    Ok(())\n}\n\n/// Encode a single property column, dispatching on variant.\n#[hotpath::measure]\nfn write_prop(prop: &StagedProperty, enc: &mut Encoder, codecs: &mut Codecs) -> MltResult<()> {\n    use ColumnType as CT;\n    use StagedProperty as D;\n\n    match prop {\n        D::Bool(v) => {\n            enc.write_column_header(CT::Bool, &v.name)?;\n            let values = v.values.iter().copied();\n            codecs.write_bool_stream(values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::OptBool(v) => {\n            codecs.begin_opt_col(CT::OptBool, &v.name, &v.presence, enc)?;\n            let values = v.values.iter().copied();\n            codecs.write_bool_stream(values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::F32(v) => {\n            enc.write_column_header(CT::F32, &v.name)?;\n            codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::OptF32(v) => {\n            codecs.begin_opt_col(CT::OptF32, &v.name, &v.presence, enc)?;\n            codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::F64(v) => {\n            enc.write_column_header(CT::F64, &v.name)?;\n            codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::OptF64(v) => {\n            codecs.begin_opt_col(CT::OptF64, &v.name, &v.presence, enc)?;\n            codecs.write_float_stream(&v.values, StreamType::Data(DictionaryType::None), enc)\n        }\n        D::I8(v) => {\n            enc.write_column_header(CT::I8, &v.name)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::OptI8(v) => {\n            codecs.begin_opt_col(CT::OptI8, &v.name, &v.presence, enc)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::U8(v) => {\n            enc.write_column_header(CT::U8, &v.name)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::OptU8(v) => {\n            codecs.begin_opt_col(CT::OptU8, &v.name, &v.presence, enc)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::I32(v) => {\n            enc.write_column_header(CT::I32, &v.name)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::OptI32(v) => {\n            codecs.begin_opt_col(CT::OptI32, &v.name, &v.presence, enc)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::U32(v) => codecs.write_u32_scalar_col(CT::U32, Some(&v.name), v, enc),\n        D::OptU32(v) => codecs.write_opt_u32_scalar_col(CT::OptU32, Some(&v.name), v, enc),\n        D::I64(v) => {\n            enc.write_column_header(CT::I64, &v.name)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::OptI64(v) => {\n            codecs.begin_opt_col(CT::OptI64, &v.name, &v.presence, enc)?;\n            codecs.write_int_stream(&v.values, &StreamCtx::prop_data(&v.name), enc)\n        }\n        D::U64(v) => codecs.write_u64_scalar_col(CT::U64, Some(&v.name), v, enc),\n        D::OptU64(v) => codecs.write_opt_u64_scalar_col(CT::OptU64, Some(&v.name), v, enc),\n        D::Str(v) => {\n            enc.write_column_header(ColumnType::Str, &v.name)?;\n            codecs.write_str_col(v, None, enc)\n        }\n        D::OptStr(v) => {\n            enc.write_column_header(ColumnType::OptStr, &v.name)?;\n            codecs.write_str_col(v, Some(v), enc)\n        }\n        D::SharedDict(v) => codecs.write_shared_dict(v, enc),\n    }\n}\n\nimpl Codecs {\n    /// Writes the column-type byte, name, and presence stream for an optional column.\n    ///\n    /// Presence is always written: the invariant is that an optional-variant column always\n    /// has a presence stream. Ensuring the column is non-empty and not all-null is the\n    /// caller's responsibility.\n    fn begin_opt_col(\n        &mut self,\n        ct: ColumnType,\n        name: &str,\n        presence: &[bool],\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        enc.write_column_header(ct, name)?;\n        self.write_presence_stream(presence.iter().copied(), enc)\n    }\n\n    pub(crate) fn write_u32_scalar_col(\n        &mut self,\n        ct: ColumnType,\n        name: Option<&str>,\n        v: &StagedScalar<u32>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        begin_scalar_col(ct, name, enc)?;\n        self.write_int_stream(&v.values, &scalar_ctx(name), enc)\n    }\n\n    pub(crate) fn write_opt_u32_scalar_col(\n        &mut self,\n        ct: ColumnType,\n        name: Option<&str>,\n        v: &StagedOptScalar<u32>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        begin_scalar_col(ct, name, enc)?;\n        self.write_presence_stream(v.presence.iter().copied(), enc)?;\n        self.write_int_stream(&v.values, &scalar_ctx(name), enc)\n    }\n\n    pub(crate) fn write_u64_scalar_col(\n        &mut self,\n        ct: ColumnType,\n        name: Option<&str>,\n        v: &StagedScalar<u64>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        begin_scalar_col(ct, name, enc)?;\n        self.write_int_stream(&v.values, &scalar_ctx(name), enc)\n    }\n\n    pub(crate) fn write_opt_u64_scalar_col(\n        &mut self,\n        ct: ColumnType,\n        name: Option<&str>,\n        v: &StagedOptScalar<u64>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        let presence_bools = v.presence.iter().copied();\n        begin_scalar_col(ct, name, enc)?;\n        self.write_presence_stream(presence_bools, enc)?;\n        self.write_int_stream(&v.values, &scalar_ctx(name), enc)\n    }\n}\n\nfn begin_scalar_col(ct: ColumnType, name: Option<&str>, enc: &mut Encoder) -> MltResult<()> {\n    if let Some(name) = name {\n        enc.write_column_header(ct, name)\n    } else {\n        enc.write_column_type(ct)\n    }\n}\n\nfn scalar_ctx(name: Option<&str>) -> StreamCtx<'_> {\n    match name {\n        Some(name) => StreamCtx::prop_data(name),\n        None => StreamCtx::id(StreamType::Data(DictionaryType::None)),\n    }\n}\n\nimpl StagedProperty {\n    #[must_use]\n    pub fn bool(name: impl Into<String>, values: Vec<bool>) -> Self {\n        Self::Bool(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn i8(name: impl Into<String>, values: Vec<i8>) -> Self {\n        Self::I8(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn u8(name: impl Into<String>, values: Vec<u8>) -> Self {\n        Self::U8(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn i32(name: impl Into<String>, values: Vec<i32>) -> Self {\n        Self::I32(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn u32(name: impl Into<String>, values: Vec<u32>) -> Self {\n        Self::U32(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn i64(name: impl Into<String>, values: Vec<i64>) -> Self {\n        Self::I64(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn u64(name: impl Into<String>, values: Vec<u64>) -> Self {\n        Self::U64(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn f32(name: impl Into<String>, values: Vec<f32>) -> Self {\n        Self::F32(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn f64(name: impl Into<String>, values: Vec<f64>) -> Self {\n        Self::F64(StagedScalar {\n            name: name.into(),\n            values,\n        })\n    }\n    #[must_use]\n    pub fn str(name: impl Into<String>, values: impl IntoIterator<Item = impl AsRef<str>>) -> Self {\n        Self::Str(StagedStrings::from_strings(name, values))\n    }\n\n    #[must_use]\n    pub fn opt_str(\n        name: impl Into<String>,\n        values: impl IntoIterator<Item = Option<impl AsRef<str>>>,\n    ) -> Self {\n        Self::OptStr(StagedStrings::from_optional(name, values))\n    }\n\n    // ── Optional constructors ─────────────────────────────────────────────────\n\n    #[must_use]\n    pub fn opt_bool(\n        name: impl Into<String>,\n        values: impl IntoIterator<Item = Option<bool>>,\n    ) -> Self {\n        Self::OptBool(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_i8(name: impl Into<String>, values: impl IntoIterator<Item = Option<i8>>) -> Self {\n        Self::OptI8(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_u8(name: impl Into<String>, values: impl IntoIterator<Item = Option<u8>>) -> Self {\n        Self::OptU8(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_i32(name: impl Into<String>, values: impl IntoIterator<Item = Option<i32>>) -> Self {\n        Self::OptI32(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_u32(name: impl Into<String>, values: impl IntoIterator<Item = Option<u32>>) -> Self {\n        Self::OptU32(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_i64(name: impl Into<String>, values: impl IntoIterator<Item = Option<i64>>) -> Self {\n        Self::OptI64(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_u64(name: impl Into<String>, values: impl IntoIterator<Item = Option<u64>>) -> Self {\n        Self::OptU64(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_f32(name: impl Into<String>, values: impl IntoIterator<Item = Option<f32>>) -> Self {\n        Self::OptF32(StagedOptScalar::from_optional(name, values))\n    }\n    #[must_use]\n    pub fn opt_f64(name: impl Into<String>, values: impl IntoIterator<Item = Option<f64>>) -> Self {\n        Self::OptF64(StagedOptScalar::from_optional(name, values))\n    }\n\n    #[must_use]\n    pub fn name(&self) -> &str {\n        match self {\n            Self::Bool(v) => &v.name,\n            Self::I8(v) => &v.name,\n            Self::U8(v) => &v.name,\n            Self::I32(v) => &v.name,\n            Self::U32(v) => &v.name,\n            Self::I64(v) => &v.name,\n            Self::U64(v) => &v.name,\n            Self::F32(v) => &v.name,\n            Self::F64(v) => &v.name,\n            Self::OptBool(v) => &v.name,\n            Self::OptI8(v) => &v.name,\n            Self::OptU8(v) => &v.name,\n            Self::OptI32(v) => &v.name,\n            Self::OptU32(v) => &v.name,\n            Self::OptI64(v) => &v.name,\n            Self::OptU64(v) => &v.name,\n            Self::OptF32(v) => &v.name,\n            Self::OptF64(v) => &v.name,\n            Self::Str(v) | Self::OptStr(v) => &v.name,\n            Self::SharedDict(v) => &v.prefix,\n        }\n    }\n}\n\nimpl<T: Copy + PartialEq> StagedOptScalar<T> {\n    /// Build from optional values: dense non-null entries in `values`,\n    /// per-feature flags in `presence`.\n    #[must_use]\n    pub fn from_optional(\n        name: impl Into<String>,\n        values: impl IntoIterator<Item = Option<T>>,\n    ) -> Self {\n        let values = values.into_iter();\n        let (lower, upper) = values.size_hint();\n        let capacity = upper.unwrap_or(lower);\n        let mut presence = Vec::with_capacity(capacity);\n        let mut dense = Vec::with_capacity(capacity);\n        for value in values {\n            presence.push(value.is_some());\n            if let Some(value) = value {\n                dense.push(value);\n            }\n        }\n        Self::from_parts(name, presence, dense)\n    }\n\n    #[must_use]\n    pub(crate) fn from_parts(name: impl Into<String>, presence: Vec<bool>, values: Vec<T>) -> Self {\n        Self {\n            name: name.into(),\n            presence,\n            values,\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/mod.rs",
    "content": "pub(crate) mod encode;\nmod model;\nmod shared_dict;\nmod strings;\n#[cfg(test)]\nmod tests;\n\npub use model::{\n    StagedOptScalar, StagedProperty, StagedScalar, StagedSharedDict, StagedSharedDictItem,\n    StagedStrings,\n};\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/model.rs",
    "content": "use crate::DictRange;\n\n/// Staged property column (encode-side, fully owned).\n///\n/// Unlike `ParsedProperty` (decode-side, potentially borrowed), all string names\n/// and corpus data are owned strings.  No lifetime parameter needed.\n#[derive(Debug, Clone, PartialEq, strum::IntoStaticStr)]\n#[strum(serialize_all = \"snake_case\")]\npub enum StagedProperty {\n    Bool(StagedScalar<bool>),\n    I8(StagedScalar<i8>),\n    U8(StagedScalar<u8>),\n    I32(StagedScalar<i32>),\n    U32(StagedScalar<u32>),\n    I64(StagedScalar<i64>),\n    U64(StagedScalar<u64>),\n    F32(StagedScalar<f32>),\n    F64(StagedScalar<f64>),\n    Str(StagedStrings),\n    OptBool(StagedOptScalar<bool>),\n    OptI8(StagedOptScalar<i8>),\n    OptU8(StagedOptScalar<u8>),\n    OptI32(StagedOptScalar<i32>),\n    OptU32(StagedOptScalar<u32>),\n    OptI64(StagedOptScalar<i64>),\n    OptU64(StagedOptScalar<u64>),\n    OptF32(StagedOptScalar<f32>),\n    OptF64(StagedOptScalar<f64>),\n    OptStr(StagedStrings),\n    SharedDict(StagedSharedDict),\n}\n\n/// Owned non-optional scalar column prepared for encoding (bool, integer, or float).\n///\n/// Every feature in this column has a value; there are no nulls.\n#[derive(Debug, Clone, PartialEq)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\npub struct StagedScalar<T: Copy + PartialEq> {\n    pub(crate) name: String,\n    pub values: Vec<T>,\n}\n\n/// Owned optional scalar column prepared for encoding (bool, integer, or float).\n///\n/// `values` contains only the non-null (present) values in dense order.\n/// `presence[i]` is `true` when feature `i` has a value; the corresponding dense\n/// entry is `values[k]` where `k` is the count of `true` entries before index `i`.\n#[derive(Debug, Clone, PartialEq)]\npub struct StagedOptScalar<T: Copy + PartialEq> {\n    pub(crate) name: String,\n    pub presence: Vec<bool>,\n    pub values: Vec<T>,\n}\n\n/// Owned non-optional string column prepared for encoding.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct StagedStrings {\n    pub(crate) name: String,\n    /// Per-feature cumulative end offsets into `data` (same encoding as `ParsedStrings::lengths`).\n    pub lengths: Vec<i32>,\n    pub(crate) data: String,\n}\n\n/// Owned shared-dictionary column prepared for encoding.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct StagedSharedDict {\n    pub(crate) prefix: String,\n    pub(crate) data: String,\n    pub items: Vec<StagedSharedDictItem>,\n}\n\n/// A single child within a staged shared-dictionary column.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct StagedSharedDictItem {\n    pub(crate) suffix: String,\n    /// Per-feature byte ranges into the shared corpus.\n    pub ranges: Vec<DictRange>,\n    /// It's OK to write unneeded one, but can't be false with nulls.\n    pub(crate) has_presence: bool,\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/shared_dict.rs",
    "content": "//! Optimizer that groups string columns into shared dictionaries using `MinHash`\n//! similarity, then hands off to per-column auto-encoders.\n\nuse std::collections::HashMap;\n\nuse integer_encoding::VarIntWriter as _;\nuse probabilistic_collections::SipHasherBuilder;\nuse probabilistic_collections::similarity::MinHash;\nuse union_find::{QuickUnionUf, UnionBySize, UnionFind as _};\n\nuse crate::MltError::DictIndexOutOfBounds;\nuse crate::codecs::fsst::compress_fsst_with;\nuse crate::decoder::strings::{decode_shared_dict_range, encode_shared_dict_range};\nuse crate::decoder::{PropValue, TileLayer};\nuse crate::encoder::model::{StrEncoding, StreamCtx};\nuse crate::encoder::optimizer::{Presence, PropertyStats, SharedDictRole};\nuse crate::encoder::property::strings::{fsst_try_train, write_fsst_data, write_raw_str_data};\nuse crate::encoder::{Codecs, Encoder, StagedSharedDict, StagedSharedDictItem};\nuse crate::errors::AsMltError as _;\nuse crate::utils::{AsUsize as _, checked_sum3, strings_to_lengths};\nuse crate::{ColumnType, DictRange, DictionaryType, LengthType, MltResult, OffsetType, StreamType};\n\n/// Number of [`MinHash`] permutations. 128 gives ~9 % error on Jaccard estimates.\nconst MINHASH_PERMUTATIONS: usize = 128;\n\n/// String columns whose estimated Jaccard similarity exceeds this threshold are\n/// grouped into a single shared dictionary.\nconst MINHASH_SIMILARITY_THRESHOLD: f64 = 0.075;\n\nstruct StringProfile<'a> {\n    col_idx: usize,\n    name: &'a str,\n    /// `MinHash` over exact string values.\n    exact_hashes: Vec<u64>,\n    /// `MinHash` over byte trigrams (empty when all strings are shorter than 3 bytes).\n    trigram_hashes: Vec<u64>,\n}\n\nimpl TileLayer {\n    /// Compute which string columns can be merged into a shared dict.\n    #[hotpath::measure]\n    pub(crate) fn group_string_properties(&self, properties: &mut [PropertyStats]) {\n        let exact_mh = MinHash::with_hashers(\n            MINHASH_PERMUTATIONS,\n            [\n                SipHasherBuilder::from_seed(0, 0),\n                SipHasherBuilder::from_seed(1, 1),\n            ],\n        );\n        let trigram_mh = MinHash::with_hashers(\n            MINHASH_PERMUTATIONS,\n            [\n                SipHasherBuilder::from_seed(0, 0),\n                SipHasherBuilder::from_seed(1, 1),\n            ],\n        );\n\n        let profiles: Vec<StringProfile<'_>> = self\n            .property_names\n            .iter()\n            .enumerate()\n            .filter_map(|(col_idx, name)| {\n                let vals: Vec<&str> = self\n                    .features\n                    .iter()\n                    .filter_map(|f| match f.properties.get(col_idx) {\n                        Some(PropValue::Str(Some(s))) => Some(s.as_str()),\n                        _ => None,\n                    })\n                    .collect();\n                if vals.is_empty() {\n                    return None;\n                }\n                let exact_hashes = exact_mh.get_min_hashes(vals.iter().copied());\n                let trigrams: Vec<[u8; 3]> = vals\n                    .iter()\n                    .flat_map(|s| s.as_bytes().windows(3).map(|w| [w[0], w[1], w[2]]))\n                    .collect();\n                let trigram_hashes = if trigrams.is_empty() {\n                    Vec::new()\n                } else {\n                    trigram_mh.get_min_hashes(trigrams.into_iter())\n                };\n                Some(StringProfile {\n                    col_idx,\n                    name,\n                    exact_hashes,\n                    trigram_hashes,\n                })\n            })\n            .collect();\n\n        for group in cluster_by_similarity(profiles) {\n            debug_assert!(\n                group\n                    .iter()\n                    .all(|p| properties[p.col_idx].presence != Presence::AllNull)\n            );\n            let owner_col = group[0].col_idx;\n            properties[owner_col]\n                .stats\n                .set_shared_dict(SharedDictRole::Owner(common_prefix_name(&group)));\n            for profile in group.iter().skip(1) {\n                properties[profile.col_idx]\n                    .stats\n                    .set_shared_dict(SharedDictRole::Member(owner_col));\n            }\n        }\n    }\n}\n\n/// Estimate Jaccard similarity from two `MinHash` signature vectors.\n#[allow(clippy::cast_precision_loss)]\nfn minhash_similarity(a: &[u64], b: &[u64]) -> f64 {\n    if a.is_empty() || b.is_empty() {\n        return 0.0;\n    }\n    let matches = a.iter().zip(b).filter(|(x, y)| x == y).count();\n    matches as f64 / a.len() as f64\n}\n\nfn cluster_by_similarity(profiles: Vec<StringProfile<'_>>) -> Vec<Vec<StringProfile<'_>>> {\n    if profiles.is_empty() {\n        return Vec::new();\n    }\n    let n = profiles.len();\n    let mut uf = QuickUnionUf::<UnionBySize>::new(n);\n\n    for i in 0..n {\n        for j in (i + 1)..n {\n            let exact = minhash_similarity(&profiles[i].exact_hashes, &profiles[j].exact_hashes);\n            let tri = minhash_similarity(&profiles[i].trigram_hashes, &profiles[j].trigram_hashes);\n            if f64::max(exact, tri) > MINHASH_SIMILARITY_THRESHOLD {\n                uf.union(i, j);\n            }\n        }\n    }\n\n    let mut groups_map = HashMap::<usize, Vec<StringProfile<'_>>>::new();\n    for (i, profile) in profiles.into_iter().enumerate() {\n        let root = uf.find(i);\n        groups_map.entry(root).or_default().push(profile);\n    }\n\n    let mut groups: Vec<Vec<StringProfile<'_>>> = groups_map\n        .into_values()\n        .filter_map(|mut v| {\n            if v.len() >= 2 {\n                v.sort_unstable_by_key(|p| p.col_idx);\n                Some(v)\n            } else {\n                None\n            }\n        })\n        .collect();\n\n    groups.sort_unstable_by_key(|g| g[0].col_idx);\n    groups\n}\n\n/// Returns the longest common byte prefix of `names`.\nfn common_prefix_name(profiles: &[StringProfile<'_>]) -> String {\n    debug_assert!(!profiles.is_empty());\n    let first = profiles[0].name;\n    let mut prefix_len = first.len();\n    for p in &profiles[1..] {\n        let new_len = first\n            .chars()\n            .zip(p.name.chars())\n            .take_while(|(a, b)| a == b)\n            .count();\n        prefix_len = prefix_len.min(new_len);\n        if prefix_len == 0 {\n            return String::new();\n        }\n    }\n    first[..first.floor_char_boundary(prefix_len)].to_owned()\n}\n\nimpl StagedSharedDict {\n    #[must_use]\n    pub fn corpus(&self) -> &str {\n        &self.data\n    }\n\n    #[must_use]\n    pub fn get(&self, span: (u32, u32)) -> Option<&str> {\n        self.corpus().get(span.0.as_usize()..span.1.as_usize())\n    }\n}\n\npub fn collect_staged_shared_dict_spans(items: &[StagedSharedDictItem]) -> Vec<(u32, u32)> {\n    let mut spans = items\n        .iter()\n        .flat_map(StagedSharedDictItem::dense_spans)\n        .collect::<Vec<_>>();\n    spans.sort_unstable();\n    spans.dedup();\n    spans\n}\n\nimpl StagedSharedDictItem {\n    #[must_use]\n    pub fn feature_count(&self) -> usize {\n        self.ranges.len()\n    }\n\n    pub fn has_presence(&self) -> bool {\n        self.has_presence\n    }\n    #[cfg(feature = \"__private\")]\n    pub fn set_presence(&mut self, value: bool) {\n        self.has_presence = value;\n    }\n\n    pub fn presence_bools(&self) -> impl ExactSizeIterator<Item = bool> + '_ {\n        self.ranges\n            .iter()\n            .map(|&range| decode_shared_dict_range(range).is_some())\n    }\n\n    pub fn dense_spans(&self) -> impl Iterator<Item = (u32, u32)> + '_ {\n        self.ranges\n            .iter()\n            .filter_map(|&range| decode_shared_dict_range(range))\n    }\n}\n\nimpl StagedSharedDict {\n    /// Build a shared-dictionary column directly from raw per-column string data.\n    ///\n    /// Each column is a `(suffix, values, presence)` tuple where `values` is an iterator\n    /// of optional strings (one per feature).  All unique non-null strings across every\n    /// column are deduplicated into a shared byte corpus; per-feature byte-range offsets\n    /// into that corpus are recorded in each shared-dictionary item.\n    pub fn new<S, I, T>(\n        prefix: impl Into<String>,\n        columns: impl IntoIterator<Item = (S, I, Presence)>,\n    ) -> MltResult<Self>\n    where\n        S: Into<String>,\n        I: IntoIterator<Item = Option<T>>,\n        T: AsRef<str>,\n    {\n        let prefix = prefix.into();\n        let mut dict_index = HashMap::<String, (u32, u32)>::new();\n        let mut data = String::new();\n\n        let items = columns\n            .into_iter()\n            .map(\n                |(suffix, values, presence)| -> MltResult<StagedSharedDictItem> {\n                    let values = values.into_iter();\n                    let (lower, upper) = values.size_hint();\n                    let mut ranges = Vec::with_capacity(upper.unwrap_or(lower));\n                    for opt_val in values {\n                        match opt_val {\n                            Some(value) => {\n                                let s = value.as_ref();\n                                let (start, end) = if let Some(&span) = dict_index.get(s) {\n                                    span\n                                } else {\n                                    let start = u32::try_from(data.len()).or_overflow()?;\n                                    let end = start\n                                        .checked_add(u32::try_from(s.len()).or_overflow()?)\n                                        .or_overflow()?;\n                                    data.push_str(s);\n                                    dict_index.insert(s.to_owned(), (start, end));\n                                    (start, end)\n                                };\n                                ranges.push(encode_shared_dict_range(start, end)?);\n                            }\n                            None => ranges.push(DictRange::NULL),\n                        }\n                    }\n                    Ok(StagedSharedDictItem {\n                        suffix: suffix.into(),\n                        ranges,\n                        has_presence: presence != Presence::AllPresent,\n                    })\n                },\n            )\n            .collect::<Result<Vec<_>, _>>()?;\n\n        Ok(Self {\n            prefix,\n            data,\n            items,\n        })\n    }\n}\n\nimpl Codecs {\n    /// Encode a shared-dictionary property and write it to `enc`.\n    ///\n    /// When [`Encoder::override_str_enc`] returns [`None`], auto-selects the corpus encoding (FSST if viable, else plain)\n    /// and uses automatic offset encoders.\n    /// When [`Some`], uses the caller-specified encoding and [`Encoder::override_int_enc`] for offsets.\n    ///\n    /// The caller (staging) is responsible for not creating empty `StagedSharedDict` instances.\n    #[hotpath::measure]\n    pub(crate) fn write_shared_dict(\n        &mut self,\n        shared_dict: &StagedSharedDict,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        let dict_spans = collect_staged_shared_dict_spans(&shared_dict.items);\n        let dict: Vec<&str> = dict_spans\n            .iter()\n            .map(|&span| {\n                shared_dict\n                    .get(span)\n                    .ok_or(DictIndexOutOfBounds(span.0, dict_spans.len()))\n            })\n            .collect::<Result<_, _>>()?;\n        let dict_index: HashMap<(u32, u32), u32> =\n            dict_spans.iter().copied().zip(0_u32..).collect();\n\n        // Decide corpus encoding upfront to determine the stream count for the varint header.\n        // FSST uses 4 streams; plain uses 2.\n        let str_enc_override = enc.override_str_enc(&shared_dict.prefix);\n        let fsst_raw = match str_enc_override {\n            Some(StrEncoding::Fsst | StrEncoding::FsstDict) => {\n                let byte_slices: Vec<&[u8]> = dict.iter().map(|s| s.as_bytes()).collect();\n                let compressor = fsst::Compressor::train(&byte_slices);\n                Some(compress_fsst_with(&dict, &compressor))\n            }\n            Some(StrEncoding::Plain | StrEncoding::Dict) => None,\n            None => {\n                // Populate cache on first sort trial, reuse on subsequent.\n                // Key includes the suffix as otherwise multiple groups could share the same prefix\n                // (e.g. two \"name:\" groups for Arabic vs Cyrillic scripts).\n                // Since the grouping is done only once, the order inside the items is deterministic, so we can just take the first suffix for the cache key.\n                let first_suffix = shared_dict.items.first().map_or(\"\", |i| &i.suffix);\n                enc.fsst_cache\n                    .entry(format!(\n                        \"{prefix}{first_suffix}\",\n                        prefix = shared_dict.prefix\n                    ))\n                    .or_insert_with(|| fsst_try_train(&dict))\n                    .as_ref()\n                    .map(|c| compress_fsst_with(&dict, c))\n            }\n        };\n        let dict_stream_count = if fsst_raw.is_some() { 4u32 } else { 2u32 };\n\n        let children_count = u32::try_from(shared_dict.items.len())?;\n        let optional_count = u32::try_from(\n            shared_dict\n                .items\n                .iter()\n                .filter(|p| p.has_presence())\n                .count(),\n        )?;\n        let stream_len = checked_sum3(dict_stream_count, children_count, optional_count)?;\n\n        // Write stream data: total count, corpus streams, then per-child streams.\n        enc.write_varint(stream_len)?;\n        if let Some(ref raw) = fsst_raw {\n            write_fsst_data(raw, DictionaryType::Single, &shared_dict.prefix, enc, self)?;\n        } else {\n            let lengths = strings_to_lengths(&dict)?;\n            let typ = StreamType::Length(LengthType::Dictionary);\n            let ctx = StreamCtx::prop(typ, &shared_dict.prefix);\n            self.write_int_stream(&lengths, &ctx, enc)?;\n            write_raw_str_data(&dict, DictionaryType::Shared, enc)?;\n        }\n\n        enc.write_column_header(ColumnType::SharedDict, &shared_dict.prefix)?;\n        enc.meta.write_varint(children_count)?;\n\n        for item in &shared_dict.items {\n            if item.has_presence() {\n                enc.write_varint(2u32)?;\n                enc.write_column_type(ColumnType::OptStr)?;\n                self.write_presence_stream(item.presence_bools(), enc)?;\n            } else {\n                enc.write_varint(1u32)?;\n                enc.write_column_type(ColumnType::Str)?;\n            }\n            enc.write_column_name(&item.suffix)?;\n\n            let offsets: Vec<u32> = item\n                .dense_spans()\n                .map(|span| {\n                    dict_index\n                        .get(&span)\n                        .copied()\n                        .ok_or(DictIndexOutOfBounds(span.0, dict_spans.len()))\n                })\n                .collect::<Result<_, _>>()?;\n            let typ = StreamType::Offset(OffsetType::String);\n            let ctx = StreamCtx::prop2(typ, &shared_dict.prefix, &item.suffix);\n            self.write_int_stream(&offsets, &ctx, enc)?;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/strings.rs",
    "content": "use fsst::Compressor;\nuse integer_encoding::VarIntWriter as _;\n\nuse super::model::StagedStrings;\nuse crate::MltResult;\nuse crate::codecs::fsst::{FsstRawData, compress_fsst, compress_fsst_with};\nuse crate::decoder::strings::{checked_string_end, encode_null_end};\nuse crate::decoder::{DictionaryType, LengthType, OffsetType, StreamMeta, StreamType};\nuse crate::encoder::model::{StrEncoding, StreamCtx};\nuse crate::encoder::stream::{dedup_strings, write_stream_payload};\nuse crate::encoder::{Codecs, Encoder};\nuse crate::utils::{AsUsize as _, strings_to_lengths};\n\n/// Minimum total raw byte size of a column before attempting FSST compression.\nconst FSST_OVERHEAD_THRESHOLD: usize = 2_048;\n/// Maximum number of strings sampled for the FSST viability probe.\nconst FSST_SAMPLE_STRINGS: usize = 256;\n\n/// Train an FSST compressor and return it when compression is likely to save space.\n///\n/// Returns `None` when the column is empty, too small for FSST overhead to pay off,\n/// or when trial compression shows no benefit.\n///\n/// Training always uses all values so the symbol table sees the full distribution.\n/// The viability probe (trial compression) is limited to [`FSST_SAMPLE_STRINGS`] to\n/// bound cost.\n#[hotpath::measure]\npub(crate) fn fsst_try_train(strings: &[&str]) -> Option<Compressor> {\n    if strings.is_empty() {\n        return None;\n    }\n    let total_plain_size: usize = strings.iter().map(|s| s.len()).sum();\n    if total_plain_size < FSST_OVERHEAD_THRESHOLD {\n        return None;\n    }\n    let byte_slices: Vec<&[u8]> = strings.iter().map(|s| s.as_bytes()).collect();\n    let compressor = Compressor::train(&byte_slices);\n    let symbols = compressor.symbol_table();\n    let symbol_lengths = compressor.symbol_lengths();\n    let symbol_overhead: usize = symbol_lengths\n        .iter()\n        .take(symbols.len())\n        .map(|&l| usize::from(l))\n        .sum();\n    let sample = if strings.len() <= FSST_SAMPLE_STRINGS {\n        strings\n    } else {\n        &strings[..FSST_SAMPLE_STRINGS]\n    };\n    let plain_size: usize = sample.iter().map(|s| s.len()).sum();\n    let compressed_size: usize = sample\n        .iter()\n        .map(|s| compressor.compress(s.as_bytes()).len())\n        .sum();\n    if symbol_overhead + compressed_size < plain_size {\n        Some(compressor)\n    } else {\n        None\n    }\n}\n\nimpl Codecs {\n    /// Encode a string column, following the same explicit-or-auto pattern as numeric columns.\n    ///\n    /// If [`Encoder::override_str_enc`] returns `Some`, only that type is encoded.\n    /// Otherwise Plain, Dict, and (when viable) FSST variants are competed via the alternatives\n    /// machinery, mirroring the `write_int_prop_*` pattern one level up.\n    #[hotpath::measure]\n    pub(crate) fn write_str_col(\n        &mut self,\n        v: &StagedStrings,\n        presence: Option<&StagedStrings>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        let non_null = v.dense_values();\n        let name = &v.name;\n        if let Some(str_enc) = enc.override_str_enc(name) {\n            match str_enc {\n                StrEncoding::Plain => write_str_plain(&non_null, presence, name, enc, self)?,\n                StrEncoding::Dict => write_str_dict(&non_null, presence, name, enc, self)?,\n                StrEncoding::Fsst => write_str_fsst(&non_null, presence, name, enc, self)?,\n                StrEncoding::FsstDict => write_str_fsst_dict(&non_null, presence, name, enc, self)?,\n            }\n        } else {\n            // Dedup once; reused by Dict and FSST+Dict alternatives.\n            let (unique, offset_indices) = dedup_strings(&non_null)?;\n\n            // Train on deduplicated values once; cached across sort trials.\n            let compressor = enc\n                .fsst_cache\n                .entry(name.clone())\n                .or_insert_with(|| fsst_try_train(&unique));\n\n            // Pre-compute compressed data while cache is accessible (before try_alternatives\n            // borrows enc). The FsstRawData is owned, so the cache borrow ends here.\n            let count = non_null.len();\n            let plain_fsst = compressor\n                .as_ref()\n                .map(|c| compress_fsst_with(&non_null, c));\n            let dict_fsst = compressor.as_ref().map(|c| compress_fsst_with(&unique, c));\n\n            let mut alt = enc.try_alternatives();\n            alt.with(|enc| write_str_plain(&non_null, presence, name, enc, self))?;\n            alt.with(|enc| {\n                write_str_dict_raw(&unique, &offset_indices, presence, name, enc, self)\n            })?;\n\n            if let Some(ref raw) = plain_fsst {\n                alt.with(|enc| write_str_fsst_raw(raw, count, presence, name, enc, self))?;\n            }\n            if let Some(ref raw) = dict_fsst {\n                alt.with(|enc| {\n                    write_str_fsst_dict_raw(raw, &offset_indices, presence, name, enc, self)\n                })?;\n            }\n        }\n        Ok(())\n    }\n}\n\n/// Encode with plain (`VarBinary` lengths) layout.\n///\n/// Stream count varint is written first, then presence, then the lengths stream\n/// (via [`Codecs::write_int_stream`] which handles the explicit/auto dispatch internally),\n/// then the raw string bytes as a plain unencoded data stream.\n#[hotpath::measure]\nfn write_str_plain(\n    non_null: &[&str],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let lengths = strings_to_lengths(non_null)?;\n    enc.write_varint(2u32 + u32::from(presence.is_some()))?;\n    write_presence_stream(presence, enc, codecs)?;\n    let ctx = StreamCtx::prop(StreamType::Length(LengthType::VarBinary), name);\n    codecs.write_int_stream(&lengths, &ctx, enc)?;\n    write_raw_str_data(non_null, DictionaryType::None, enc)\n}\n\n/// Encode with dictionary (deduped corpus + offset indices) layout.\n#[hotpath::measure]\nfn write_str_dict(\n    non_null: &[&str],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let (unique, offset_indices) = dedup_strings(non_null)?;\n    write_str_dict_raw(&unique, &offset_indices, presence, name, enc, codecs)\n}\n\n/// Write pre-deduped dictionary data.\nfn write_str_dict_raw(\n    unique: &[&str],\n    offset_indices: &[u32],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let lengths = strings_to_lengths(unique)?;\n    enc.write_varint(3u32 + u32::from(presence.is_some()))?;\n    write_presence_stream(presence, enc, codecs)?;\n\n    let ctx = StreamCtx::prop(StreamType::Length(LengthType::Dictionary), name);\n    codecs.write_int_stream(&lengths, &ctx, enc)?;\n\n    let ctx = StreamCtx::prop(StreamType::Offset(OffsetType::String), name);\n    codecs.write_int_stream(offset_indices, &ctx, enc)?;\n    write_raw_str_data(unique, DictionaryType::Single, enc)\n}\n\n/// Encode with FSST compression, training a fresh compressor.\n///\n/// Used by the explicit-encoder path.\n#[hotpath::measure]\nfn write_str_fsst(\n    non_null: &[&str],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let raw = compress_fsst(non_null);\n    write_str_fsst_raw(&raw, non_null.len(), presence, name, enc, codecs)\n}\n\n/// Shared FSST write logic.\nfn write_str_fsst_raw(\n    raw: &FsstRawData,\n    count: usize,\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let offsets: Vec<u32> = (0..u32::try_from(count)?).collect();\n    enc.write_varint(5u32 + u32::from(presence.is_some()))?;\n    write_presence_stream(presence, enc, codecs)?;\n    write_fsst_data(raw, DictionaryType::Single, name, enc, codecs)?;\n    let ctx = StreamCtx::prop(StreamType::Offset(OffsetType::String), name);\n    codecs.write_int_stream(&offsets, &ctx, enc)\n}\n\n/// Encode with FSST + dictionary layout, training a fresh compressor.\n///\n/// Used by the explicit-encoder path.\n#[hotpath::measure]\nfn write_str_fsst_dict(\n    non_null: &[&str],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let (unique, offset_indices) = dedup_strings(non_null)?;\n    let raw = compress_fsst(&unique);\n    write_str_fsst_dict_raw(&raw, &offset_indices, presence, name, enc, codecs)\n}\n\n/// Shared FSST+dict write logic.\nfn write_str_fsst_dict_raw(\n    raw: &FsstRawData,\n    offset_indices: &[u32],\n    presence: Option<&StagedStrings>,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    enc.write_varint(5u32 + u32::from(presence.is_some()))?;\n    write_presence_stream(presence, enc, codecs)?;\n    write_fsst_data(raw, DictionaryType::Single, name, enc, codecs)?;\n    let ctx = StreamCtx::prop(StreamType::Offset(OffsetType::String), name);\n    codecs.write_int_stream(offset_indices, &ctx, enc)\n}\n\nfn write_presence_stream(\n    presence: Option<&StagedStrings>,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    if let Some(strings) = presence {\n        codecs.write_presence_stream(strings.presence_bools(), enc)?;\n    }\n    Ok(())\n}\n\n/// Write 4 FSST sub-streams directly to `enc.data`.\n///\n/// The two integer sub-streams (`symbol_lengths`, `value_lengths`) use [`Codecs::write_int_stream`]\n/// so explicit encoder overrides are honored and all candidates are competed automatically.\n/// The two raw-byte sub-streams (`symbol_table`, `corpus`) are written without integer encoding.\n///\n/// Stream order: `symbol_lengths`, `symbol_table`, `value_lengths`, `corpus`.\n#[hotpath::measure]\npub fn write_fsst_data(\n    raw: &FsstRawData,\n    dict_type: DictionaryType,\n    name: &str,\n    enc: &mut Encoder,\n    codecs: &mut Codecs,\n) -> MltResult<()> {\n    let ctx = StreamCtx::prop(StreamType::Length(LengthType::Symbol), name);\n    codecs.write_int_stream(&raw.symbol_lengths, &ctx, enc)?;\n    let typ = StreamType::Data(DictionaryType::Fsst);\n    let meta = StreamMeta::new_none(typ, raw.symbol_lengths.len())?;\n    write_stream_payload(&mut enc.data, meta, false, &raw.symbol_bytes)?;\n    let ctx = StreamCtx::prop(StreamType::Length(LengthType::Dictionary), name);\n    codecs.write_int_stream(&raw.value_lengths, &ctx, enc)?;\n    let meta = StreamMeta::new_none(StreamType::Data(dict_type), raw.value_lengths.len())?;\n    write_stream_payload(&mut enc.data, meta, false, &raw.corpus)?;\n    Ok(())\n}\n\n/// Write raw string bytes as an unencoded data stream directly to `enc.data`.\n#[hotpath::measure]\npub fn write_raw_str_data(\n    strings: &[&str],\n    dict_type: DictionaryType,\n    enc: &mut Encoder,\n) -> MltResult<()> {\n    let total_len: usize = strings.iter().map(|s| s.len()).sum();\n    let typ = StreamType::Data(dict_type);\n    let meta = StreamMeta::new_none(typ, strings.len())?;\n    meta.write_to(enc, false, u32::try_from(total_len)?)?;\n    enc.data.reserve(total_len);\n    for s in strings {\n        enc.data.extend_from_slice(s.as_bytes());\n    }\n    Ok(())\n}\n\nimpl StagedStrings {\n    /// Stages a string column where every row has a value (no nulls).\n    ///\n    /// `name` is the column key (e.g. shared-dict suffix or top-level property name).\n    ///\n    /// `values` can be any iterator of string fragments, for example `[\"a\", \"b\"]`,\n    /// `vec![\"x\".into(), \"y\".into()]`, or `some_vec.iter().map(|s| s.as_str())`.\n    #[must_use]\n    pub fn from_strings(\n        name: impl Into<String>,\n        values: impl IntoIterator<Item = impl AsRef<str>>,\n    ) -> Self {\n        let name = name.into();\n        let iter = values.into_iter();\n        let (lower, _) = iter.size_hint();\n        let mut lengths = Vec::with_capacity(lower);\n        let mut data = String::new();\n        let mut end = 0_i32;\n        for value in iter {\n            let value = value.as_ref();\n            end = checked_string_end(end, value.len())\n                .expect(\"staged string corpus exceeds supported i32 range\");\n            lengths.push(end);\n            data.push_str(value);\n        }\n        Self {\n            name,\n            lengths,\n            data,\n        }\n    }\n\n    /// Stages a string column with optional values (nulls encoded in the length stream).\n    ///\n    /// `name` is the column key (e.g. shared-dict suffix or top-level property name).\n    ///\n    /// `values` can be any iterator of optional string fragments, for example\n    /// `vec![Some(\"a\"), None]` or a `Vec<Option<String>>`.\n    #[must_use]\n    pub fn from_optional(\n        name: impl Into<String>,\n        values: impl IntoIterator<Item = Option<impl AsRef<str>>>,\n    ) -> Self {\n        let name = name.into();\n        let iter = values.into_iter();\n        let (lower, _) = iter.size_hint();\n        let mut lengths = Vec::with_capacity(lower);\n        let mut data = String::new();\n        let mut end = 0_i32;\n        for value in iter {\n            match value {\n                Some(value) => {\n                    let value = value.as_ref();\n                    end = checked_string_end(end, value.len())\n                        .expect(\"staged string corpus exceeds supported i32 range\");\n                    lengths.push(end);\n                    data.push_str(value);\n                }\n                None => lengths.push(encode_null_end(end)),\n            }\n        }\n        Self {\n            name,\n            lengths,\n            data,\n        }\n    }\n\n    #[must_use]\n    pub fn feature_count(&self) -> usize {\n        self.lengths.len()\n    }\n\n    pub fn presence_bools(&self) -> impl ExactSizeIterator<Item = bool> + '_ {\n        self.lengths.iter().map(|&end| end >= 0)\n    }\n\n    #[must_use]\n    pub fn dense_values(&self) -> Vec<&str> {\n        let mut values = Vec::new();\n        let mut start = 0_u32;\n        for &end in &self.lengths {\n            if end >= 0 {\n                let end = end.cast_unsigned();\n                values.push(&self.data[start.as_usize()..end.as_usize()]);\n                start = end;\n            } else {\n                start = (!end).cast_unsigned();\n            }\n        }\n        values\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/property/tests.rs",
    "content": "use geo_types::Point;\nuse proptest::prelude::*;\nuse rstest::rstest;\n\nuse crate::encoder::SortStrategy::Unsorted;\nuse crate::encoder::model::{ExplicitEncoder, StagedLayer, StrEncoding};\nuse crate::encoder::optimizer::{Presence, PropertyTypedStats, SharedDictRole};\nuse crate::encoder::property::encode::write_properties;\nuse crate::encoder::{\n    Codecs, Encoder, EncoderConfig, IntEncoder, LogicalEncoder, PhysicalEncoder, StagedId,\n    StagedProperty, StagedSharedDict, stage_tile,\n};\nuse crate::test_helpers::{dec, parser};\nuse crate::{DictRange, GeometryValues, Layer, MltError, PropValue, TileFeature, TileLayer};\n// proptest_derive::Arbitrary is only derived for these types inside the crate\n// under #[cfg(test)], so we write the strategies by hand here.\n\nfn arb_logical_encoder() -> impl Strategy<Value = LogicalEncoder> {\n    prop_oneof![\n        Just(LogicalEncoder::None),\n        Just(LogicalEncoder::Delta),\n        Just(LogicalEncoder::DeltaRle),\n        Just(LogicalEncoder::Rle),\n    ]\n}\n\nfn arb_physical_encoder() -> impl Strategy<Value = PhysicalEncoder> {\n    prop_oneof![\n        Just(PhysicalEncoder::None),\n        Just(PhysicalEncoder::VarInt),\n        Just(PhysicalEncoder::FastPFOR),\n    ]\n}\n\n/// [`PhysicalEncoder`] variants that are valid for 64-bit integers\n/// (i.e. everything except `FastPFOR`).\nfn arb_physical_no_fastpfor() -> impl Strategy<Value = PhysicalEncoder> {\n    prop_oneof![Just(PhysicalEncoder::None), Just(PhysicalEncoder::VarInt),]\n}\n\nfn arb_int_encoder() -> impl Strategy<Value = IntEncoder> {\n    (arb_logical_encoder(), arb_physical_encoder())\n        .prop_map(|(logical, physical)| IntEncoder::new(logical, physical))\n}\n\n/// [`IntEncoder`] strategy that excludes `FastPFOR`, which only handles 32-bit integers.\nfn arb_int_encoder_no_fastpfor() -> impl Strategy<Value = IntEncoder> {\n    (arb_logical_encoder(), arb_physical_no_fastpfor())\n        .prop_map(|(logical, physical)| IntEncoder::new(logical, physical))\n}\n\nfn staged_len(staged: &StagedProperty) -> usize {\n    match staged {\n        StagedProperty::Bool(s) => s.values.len(),\n        StagedProperty::I8(s) => s.values.len(),\n        StagedProperty::U8(s) => s.values.len(),\n        StagedProperty::I32(s) => s.values.len(),\n        StagedProperty::U32(s) => s.values.len(),\n        StagedProperty::I64(s) => s.values.len(),\n        StagedProperty::U64(s) => s.values.len(),\n        StagedProperty::F32(s) => s.values.len(),\n        StagedProperty::F64(s) => s.values.len(),\n        StagedProperty::OptBool(s) => s.presence.len(),\n        StagedProperty::OptI8(s) => s.presence.len(),\n        StagedProperty::OptU8(s) => s.presence.len(),\n        StagedProperty::OptI32(s) => s.presence.len(),\n        StagedProperty::OptU32(s) => s.presence.len(),\n        StagedProperty::OptI64(s) => s.presence.len(),\n        StagedProperty::OptU64(s) => s.presence.len(),\n        StagedProperty::OptF32(s) => s.presence.len(),\n        StagedProperty::OptF64(s) => s.presence.len(),\n        StagedProperty::Str(s) | StagedProperty::OptStr(s) => s.lengths.len(),\n        StagedProperty::SharedDict(s) => s.items.first().map_or(0, |i| i.ranges.len()),\n    }\n}\n\nfn strs(vals: &[&str]) -> Vec<Option<String>> {\n    vals.iter().map(|v| Some((*v).to_string())).collect()\n}\n\nfn opt_strs(vals: &[Option<&str>]) -> Vec<Option<String>> {\n    vals.iter().map(|v| v.map(ToString::to_string)).collect()\n}\n\nfn presence<T>(values: &[Option<T>]) -> Presence {\n    if values.iter().all(Option::is_some) {\n        Presence::AllPresent\n    } else {\n        Presence::Mixed\n    }\n}\n\nfn shared_dict_prop(name: &str, children: Vec<(String, Vec<Option<String>>)>) -> StagedProperty {\n    let children = children.into_iter().map(|(suffix, values)| {\n        let presence = presence(&values);\n        (suffix, values, presence)\n    });\n    StagedProperty::SharedDict(StagedSharedDict::new(name, children).expect(\"build shared dict\"))\n}\n\ntype SharedDictChildren = Vec<(String, Vec<Option<String>>)>;\n\nfn arb_shared_dict_children() -> impl Strategy<Value = (usize, SharedDictChildren)> {\n    (1usize..20, 1usize..5usize).prop_flat_map(|(n, child_count)| {\n        prop::collection::vec(\n            (\n                \"[a-z]{1,6}\",\n                prop::collection::vec(prop::option::of(\"[a-zA-Z ]{0,20}\"), n),\n            ),\n            child_count,\n        )\n        .prop_map(move |mut children| {\n            if children\n                .iter()\n                .all(|(_, vals)| vals.iter().all(Option::is_none))\n            {\n                children[0].1[0] = Some(String::new());\n            }\n            (n, children)\n        })\n    })\n}\n\n/// Build a `(name, values)` pair for use as a [`shared_dict_prop`] column.\nfn col(name: &str, values: Vec<Option<String>>) -> (String, Vec<Option<String>>) {\n    (name.to_string(), values)\n}\n\n/// Shorthand for a non-null [`PropValue::Str`].\nfn ps(s: &str) -> PropValue {\n    PropValue::Str(Some(s.into()))\n}\n\n/// Create a [`GeometryValues`] with `n` degenerate point features at the origin.\nfn n_point_geometry(n: usize) -> GeometryValues {\n    let mut g = GeometryValues::default();\n    for _ in 0..n {\n        g.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n    }\n    g\n}\n\n/// Encode `props` as a layer with matching point geometry and return the raw bytes.\nfn encode_to_bytes(props: Vec<StagedProperty>) -> Vec<u8> {\n    encode_to_bytes_explicit(props, ExplicitEncoder::all(IntEncoder::varint()))\n}\n\n/// Encode `props` with explicit encoder config and return the raw bytes.\nfn encode_to_bytes_explicit(props: Vec<StagedProperty>, cfg: ExplicitEncoder) -> Vec<u8> {\n    let n = props.iter().map(staged_len).max().unwrap_or(0);\n    let layer = StagedLayer {\n        name: \"test\".into(),\n        extent: 4096,\n        id: StagedId::None,\n        geometry: n_point_geometry(n),\n        properties: props,\n    };\n    let enc = Encoder::with_explicit(EncoderConfig::default(), cfg);\n    let mut codecs = Codecs::default();\n    let enc = layer\n        .encode_into(enc, &mut codecs)\n        .expect(\"encoding failed\");\n    enc.into_layer_bytes().expect(\"into_layer_bytes failed\")\n}\n\n/// Encode and immediately decode `props` into a [`TileLayer`] using auto varint encoding.\nfn encode_and_tile(props: Vec<StagedProperty>) -> TileLayer {\n    encode_and_tile_explicit(props, ExplicitEncoder::all(IntEncoder::varint()))\n}\n\n/// Encode and decode with explicit encoder config.\nfn encode_and_tile_explicit(props: Vec<StagedProperty>, cfg: ExplicitEncoder) -> TileLayer {\n    let bytes = encode_to_bytes_explicit(props, cfg);\n    let (_, layer) = Layer::from_bytes(&bytes, &mut parser()).expect(\"layer parse failed\");\n    let Layer::Tag01(layer01) = layer else {\n        panic!(\"expected Tag01 layer\")\n    };\n    let mut d = dec();\n    let parsed = layer01.decode_all(&mut d).expect(\"decode failed\");\n    parsed.into_tile(&mut d).expect(\"into_tile failed\")\n}\n\n// Absent mode has no presence stream on the wire, so only all-Some inputs are\n// valid for those variants.\nmacro_rules! integer_roundtrip_proptests {\n    ($present:ident, $absent:ident, $variant:ident, $opt_fn:ident, $non_opt_fn:ident, $ty:ty, $int_encoder:expr) => {\n        proptest! {\n            #[test]\n            fn $present(\n                values in prop::collection::vec(prop::option::of(any::<$ty>()), 1..100),\n                enc in $int_encoder,\n            ) {\n                // All-null columns are skipped in encoding; only test when at\n                // least one value is present.\n                prop_assume!(values.iter().any(Option::is_some));\n                let tile = encode_and_tile_explicit(\n                    vec![StagedProperty::$opt_fn(\"x\", values.clone())],\n                    ExplicitEncoder::all(enc),\n                );\n                prop_assert_eq!(&tile.property_names, &[\"x\"]);\n                for (i, ov) in values.into_iter().enumerate() {\n                    prop_assert_eq!(&tile.features[i].properties[0], &PropValue::$variant(ov));\n                }\n            }\n\n            #[test]\n            fn $absent(\n                values in prop::collection::vec(any::<$ty>(), 1..100),\n                enc in $int_encoder,\n            ) {\n                let tile = encode_and_tile_explicit(\n                    vec![StagedProperty::$non_opt_fn(\"x\", values.clone())],\n                    ExplicitEncoder::all(enc),\n                );\n                prop_assert_eq!(&tile.property_names, &[\"x\"]);\n                for (i, &v) in values.iter().enumerate() {\n                    prop_assert_eq!(&tile.features[i].properties[0], &PropValue::$variant(Some(v)));\n                }\n            }\n        }\n    };\n}\n\n// i8, u8, i32, u32 — all physical encoders are valid.\ninteger_roundtrip_proptests!(i8_present, i8_absent, I8, opt_i8, i8, i8, arb_int_encoder());\ninteger_roundtrip_proptests!(u8_present, u8_absent, U8, opt_u8, u8, u8, arb_int_encoder());\ninteger_roundtrip_proptests!(\n    i32_present,\n    i32_absent,\n    I32,\n    opt_i32,\n    i32,\n    i32,\n    arb_int_encoder()\n);\ninteger_roundtrip_proptests!(\n    u32_present,\n    u32_absent,\n    U32,\n    opt_u32,\n    u32,\n    u32,\n    arb_int_encoder()\n);\n// FastPFOR does not support 64-bit integers.\ninteger_roundtrip_proptests!(\n    i64_present,\n    i64_absent,\n    I64,\n    opt_i64,\n    i64,\n    i64,\n    arb_int_encoder_no_fastpfor()\n);\ninteger_roundtrip_proptests!(\n    u64_present,\n    u64_absent,\n    U64,\n    opt_u64,\n    u64,\n    u64,\n    arb_int_encoder_no_fastpfor()\n);\n\n#[test]\nfn bool_specific_values() {\n    let values = vec![Some(true), None, Some(false), Some(true), None];\n    let tile = encode_and_tile(vec![StagedProperty::opt_bool(\"active\", values.clone())]);\n    assert_eq!(tile.property_names, vec![\"active\"]);\n    for (i, ov) in values.into_iter().enumerate() {\n        assert_eq!(&tile.features[i].properties[0], &PropValue::Bool(ov));\n    }\n}\n\nproptest! {\n    #[test]\n    fn bool_roundtrip(\n        values in prop::collection::vec(prop::option::of(any::<bool>()), 1..100),\n    ) {\n        // All-null columns are skipped; only test when at least one value is present.\n        prop_assume!(values.iter().any(Option::is_some));\n        let tile = encode_and_tile(vec![StagedProperty::opt_bool(\"flag\", values.clone())]);\n        prop_assert_eq!(&tile.property_names, &[\"flag\"]);\n        for (i, ov) in values.into_iter().enumerate() {\n            prop_assert_eq!(&tile.features[i].properties[0], &PropValue::Bool(ov));\n        }\n    }\n}\n\n// NaN is excluded because NaN != NaN.\nproptest! {\n    #[test]\n    fn f32_roundtrip(\n        values in prop::collection::vec(\n            prop::option::of(any::<f32>().prop_filter(\"no NaN\", |f| !f.is_nan())),\n            1..100,\n        ),\n    ) {\n        // All-null columns are skipped; only test when at least one value is present.\n        prop_assume!(values.iter().any(Option::is_some));\n        let tile = encode_and_tile(vec![StagedProperty::opt_f32(\"score\", values.clone())]);\n        prop_assert_eq!(&tile.property_names, &[\"score\"]);\n        for (i, ov) in values.into_iter().enumerate() {\n            prop_assert_eq!(&tile.features[i].properties[0], &PropValue::F32(ov));\n        }\n    }\n\n    #[test]\n    fn f64_roundtrip(\n        values in prop::collection::vec(\n            prop::option::of(any::<f64>().prop_filter(\"no NaN\", |f| !f.is_nan())),\n            1..100,\n        ),\n    ) {\n        // All-null columns are skipped; only test when at least one value is present.\n        prop_assume!(values.iter().any(Option::is_some));\n        let tile = encode_and_tile(vec![StagedProperty::opt_f64(\"score\", values.clone())]);\n        prop_assert_eq!(&tile.property_names, &[\"score\"]);\n        for (i, ov) in values.into_iter().enumerate() {\n            prop_assert_eq!(&tile.features[i].properties[0], &PropValue::F64(ov));\n        }\n    }\n}\n\n#[test]\nfn str_scalar_with_nulls() {\n    let values = opt_strs(&[Some(\"Berlin\"), None, Some(\"Hamburg\"), None]);\n    let tile = encode_and_tile(vec![StagedProperty::opt_str(\"city\", values.clone())]);\n    assert_eq!(tile.property_names, vec![\"city\"]);\n    for (i, ov) in values.into_iter().enumerate() {\n        assert_eq!(&tile.features[i].properties[0], &PropValue::Str(ov));\n    }\n}\n\n#[test]\nfn str_scalar_empty() {\n    // Staging an empty column is a no-op at the staging layer (build_scalar_column\n    // never produces empty StagedProperty::str; this test exercises the case where\n    // a tile has zero features — the round-trip must not panic).\n    let tile = encode_and_tile(vec![StagedProperty::str(\n        \"unused\",\n        std::iter::empty::<&str>(),\n    )]);\n    // Zero features → zero properties should be visible after decoding\n    assert!(tile.features.is_empty());\n}\n\nproptest! {\n    #[test]\n    fn str_scalar_roundtrip_non_null(\n        values in prop::collection::vec(\"[a-zA-Z0-9 ]{0,30}\", 1..50),\n    ) {\n        let tile = encode_and_tile(vec![StagedProperty::str(\"name\", values.clone())]);\n        prop_assert_eq!(&tile.property_names, &[\"name\"]);\n        for (i, v) in values.into_iter().enumerate() {\n            prop_assert_eq!(&tile.features[i].properties[0], &PropValue::Str(Some(v)));\n        }\n    }\n\n    #[test]\n    fn str_scalar_roundtrip_with_nulls(\n        values in prop::collection::vec(prop::option::of(\"[a-zA-Z0-9 ]{0,30}\"), 1..50),\n    ) {\n        prop_assume!(values.iter().any(Option::is_some));\n        let tile = encode_and_tile(vec![StagedProperty::opt_str(\"name\", values.clone())]);\n        prop_assert_eq!(&tile.property_names, &[\"name\"]);\n        for (i, ov) in values.into_iter().enumerate() {\n            prop_assert_eq!(&tile.features[i].properties[0], &PropValue::Str(ov));\n        }\n    }\n}\n\n#[test]\nfn fsst_scalar_string_roundtrip() {\n    let values = [\"Berlin\", \"Brandenburg\", \"Bremen\", \"Braunschweig\"];\n    let tile = encode_and_tile_explicit(\n        vec![StagedProperty::str(\"name\", values)],\n        ExplicitEncoder::all_with_str(IntEncoder::plain(), StrEncoding::Fsst),\n    );\n    assert_eq!(tile.property_names, vec![\"name\"]);\n    for (i, s) in values.iter().enumerate() {\n        assert_eq!(\n            &tile.features[i].properties[0],\n            &PropValue::Str(Some(s.to_string()))\n        );\n    }\n}\n\n/// Round-trip a two-column `SharedDict` with auto encoders and check all feature values.\nfn check_two_col_dict(\n    name: &str,\n    s1: &str,\n    vals1: Vec<Option<String>>,\n    s2: &str,\n    vals2: Vec<Option<String>>,\n) {\n    let tile = encode_and_tile(vec![shared_dict_prop(\n        name,\n        vec![col(s1, vals1.clone()), col(s2, vals2.clone())],\n    )]);\n    assert_eq!(\n        tile.property_names,\n        vec![format!(\"{name}{s1}\"), format!(\"{name}{s2}\")]\n    );\n    for (i, (v1, v2)) in vals1.into_iter().zip(vals2).enumerate() {\n        assert_eq!(&tile.features[i].properties[0], &PropValue::Str(v1));\n        assert_eq!(&tile.features[i].properties[1], &PropValue::Str(v2));\n    }\n}\n\n#[test]\nfn fsst_struct_shared_dict_roundtrip() {\n    check_two_col_dict(\n        \"name\",\n        \":de\",\n        strs(&[\"Berlin\", \"München\", \"Köln\"]),\n        \":en\",\n        strs(&[\"Berlin\", \"Munich\", \"Cologne\"]),\n    );\n}\n\n#[test]\nfn struct_with_nulls() {\n    check_two_col_dict(\n        \"name\",\n        \":de\",\n        opt_strs(&[Some(\"Berlin\"), Some(\"München\"), None]),\n        \":en\",\n        opt_strs(&[Some(\"Berlin\"), None, Some(\"London\")]),\n    );\n}\n\n#[test]\nfn struct_shared_dict_inline_ranges_track_nulls_and_empty_strings() {\n    // This test validates internal range bookkeeping in StagedSharedDict —\n    // not the byte encoding pipeline — so it inspects the staged form directly.\n    let dict = StagedSharedDict::new(\n        \"name\",\n        vec![\n            (\n                \":de\",\n                opt_strs(&[Some(\"\"), None, Some(\"Berlin\")]),\n                Presence::Mixed,\n            ),\n            (\n                \":en\",\n                opt_strs(&[Some(\"\"), Some(\"Berlin\"), Some(\"\")]),\n                Presence::AllPresent,\n            ),\n        ],\n    )\n    .unwrap();\n    let corpus = dict.corpus();\n    let [de, en] = dict.items.as_slice() else {\n        panic!(\"expected exactly 2 items\");\n    };\n\n    // de: [Some(\"\"), None, Some(\"Berlin\")]\n    assert_ne!(de.ranges[0], DictRange::NULL);\n    assert_eq!(de.ranges[0].start, de.ranges[0].end); // empty string: zero-length span\n    assert_eq!(de.ranges[1], DictRange::NULL); // null entry\n    assert_ne!(de.ranges[2], DictRange::NULL);\n    let start: usize = de.ranges[2].start.try_into().unwrap();\n    let end: usize = de.ranges[2].end.try_into().unwrap();\n    assert_eq!(&corpus[start..end], \"Berlin\");\n\n    // en: [Some(\"\"), Some(\"Berlin\"), Some(\"\")]\n    assert_ne!(en.ranges[0], DictRange::NULL);\n    assert_eq!(en.ranges[0].start, en.ranges[0].end); // empty string: zero-length span\n    assert_eq!(en.ranges[1], de.ranges[2]); // same deduped span for \"Berlin\"\n    assert_ne!(en.ranges[2], DictRange::NULL);\n    assert_eq!(en.ranges[2].start, en.ranges[2].end); // empty string: zero-length span\n}\n\n#[test]\nfn struct_no_nulls() {\n    check_two_col_dict(\n        \"name\",\n        \":de\",\n        strs(&[\"Berlin\", \"München\", \"Hamburg\"]),\n        \":en\",\n        strs(&[\"Berlin\", \"Munich\", \"Hamburg\"]),\n    );\n}\n\n#[test]\nfn struct_shared_dict_deduplication() {\n    check_two_col_dict(\n        \"name\",\n        \":de\",\n        strs(&[\"Berlin\", \"Berlin\"]),\n        \":en\",\n        strs(&[\"Berlin\", \"London\"]),\n    );\n}\n\n#[test]\nfn struct_mixed_with_scalars() {\n    let tile = encode_and_tile(vec![\n        StagedProperty::opt_u32(\"population\", vec![Some(3_748_000), Some(1_787_000_u32)]),\n        shared_dict_prop(\n            \"name:\",\n            vec![\n                col(\"de\", strs(&[\"Berlin\", \"Hamburg\"])),\n                col(\"en\", strs(&[\"Berlin\", \"Hamburg\"])),\n            ],\n        ),\n        StagedProperty::opt_u32(\"rank\", vec![Some(1_u32), Some(2)]),\n    ]);\n\n    assert_eq!(\n        tile.property_names,\n        vec![\"population\", \"name:de\", \"name:en\", \"rank\"]\n    );\n    assert_eq!(tile.features.len(), 2);\n    assert_eq!(\n        tile.features[0].properties,\n        vec![\n            PropValue::U32(Some(3_748_000)),\n            ps(\"Berlin\"),\n            ps(\"Berlin\"),\n            PropValue::U32(Some(1))\n        ]\n    );\n    assert_eq!(\n        tile.features[1].properties,\n        vec![\n            PropValue::U32(Some(1_787_000)),\n            ps(\"Hamburg\"),\n            ps(\"Hamburg\"),\n            PropValue::U32(Some(2))\n        ]\n    );\n}\n\n#[test]\nfn two_struct_groups_with_scalar_between() {\n    let tile = encode_and_tile(vec![\n        shared_dict_prop(\n            \"name:\",\n            vec![\n                col(\"de\", strs(&[\"Berlin\", \"Hamburg\"])),\n                col(\"en\", strs(&[\"Berlin\", \"Hamburg\"])),\n            ],\n        ),\n        StagedProperty::opt_u32(\"population\", vec![Some(3_748_000_u32), Some(1_787_000)]),\n        shared_dict_prop(\n            \"label:\",\n            vec![\n                col(\"de\", strs(&[\"BE\", \"HH\"])),\n                col(\"en\", strs(&[\"BER\", \"HAM\"])),\n            ],\n        ),\n    ]);\n\n    assert_eq!(\n        tile.property_names,\n        vec![\"name:de\", \"name:en\", \"population\", \"label:de\", \"label:en\"]\n    );\n    assert_eq!(tile.features.len(), 2);\n    assert_eq!(\n        tile.features[0].properties,\n        vec![\n            ps(\"Berlin\"),\n            ps(\"Berlin\"),\n            PropValue::U32(Some(3_748_000)),\n            ps(\"BE\"),\n            ps(\"BER\")\n        ]\n    );\n    assert_eq!(\n        tile.features[1].properties,\n        vec![\n            ps(\"Hamburg\"),\n            ps(\"Hamburg\"),\n            PropValue::U32(Some(1_787_000)),\n            ps(\"HH\"),\n            ps(\"HAM\")\n        ]\n    );\n}\n\n#[test]\nfn lazy_layer01_iterate_prop_names_returns_column_names() {\n    // Encode a layer with a scalar column and a two-key SharedDict column.\n    let bytes = encode_to_bytes(vec![\n        StagedProperty::opt_u32(\"pop\", vec![Some(1_000_u32), Some(2_000)]),\n        shared_dict_prop(\n            \"addr:\",\n            vec![\n                col(\"city\", strs(&[\"Berlin\", \"Rome\"])),\n                col(\"zip\", strs(&[\"10115\", \"00100\"])),\n            ],\n        ),\n    ]);\n\n    // Parse as a lazy Layer01 — no column data decoded yet.\n    let (_, layer) = Layer::from_bytes(&bytes, &mut parser()).expect(\"parse failed\");\n    let Layer::Tag01(layer) = layer else {\n        panic!(\"expected Tag01 layer\")\n    };\n\n    let names: Vec<String> = layer.iterate_prop_names().map(|n| n.to_string()).collect();\n    assert_eq!(names, [\"pop\", \"addr:city\", \"addr:zip\"]);\n}\n\nproptest! {\n    #[test]\n    fn struct_roundtrip(\n        struct_name in \"[a-z]{1,8}\",\n        input in arb_shared_dict_children(),\n    ) {\n        let (n, children) = input;\n        let staged_children = children.iter().map(|(suffix, values)| {\n            let presence = presence(values);\n            (suffix.clone(), values.clone(), presence)\n        });\n        let staged = StagedProperty::SharedDict(\n            StagedSharedDict::new(&struct_name, staged_children).expect(\"build shared dict\"),\n        );\n        let tile = encode_and_tile(vec![staged]);\n\n        let expected_names: Vec<String> = children\n            .iter()\n            .map(|(suffix, _)| format!(\"{struct_name}{suffix}\"))\n            .collect();\n        prop_assert_eq!(&tile.property_names, &expected_names);\n        prop_assert_eq!(tile.features.len(), n);\n\n        for (feat_idx, feat) in tile.features.iter().enumerate() {\n            for (col_idx, (_, values)) in children.iter().enumerate() {\n                prop_assert_eq!(\n                    &feat.properties[col_idx],\n                    &PropValue::Str(values[feat_idx].clone())\n                );\n            }\n        }\n    }\n}\n\n/// Build a [`TileLayer`] from heterogeneous column data (one `Vec<PropValue>` per column).\nfn tile_from_cols(cols: &[(&str, Vec<PropValue>)]) -> TileLayer {\n    let n = cols.first().map_or(0, |(_, v)| v.len());\n    let property_names = cols.iter().map(|(name, _)| (*name).to_string()).collect();\n    let geom = geo_types::Geometry::<i32>::Point(Point::new(0, 0));\n    let features = (0..n)\n        .map(|i| TileFeature {\n            id: None,\n            geometry: geom.clone(),\n            properties: cols.iter().map(|(_, vals)| vals[i].clone()).collect(),\n        })\n        .collect();\n    TileLayer {\n        name: \"test\".to_string(),\n        extent: 4096,\n        property_names,\n        features,\n    }\n}\n\nfn tile_from_cols_with_ids(ids: &[Option<u64>], cols: &[(&str, Vec<PropValue>)]) -> TileLayer {\n    let mut tile = tile_from_cols(cols);\n    for (feature, id) in tile.features.iter_mut().zip(ids.iter().copied()) {\n        feature.id = id;\n    }\n    tile\n}\n\nfn tile_from_ids(ids: &[Option<u64>]) -> TileLayer {\n    let geom = geo_types::Geometry::<i32>::Point(Point::new(0, 0));\n    TileLayer {\n        name: \"test\".to_string(),\n        extent: 4096,\n        property_names: vec![],\n        features: ids\n            .iter()\n            .map(|&id| TileFeature {\n                id,\n                geometry: geom.clone(),\n                properties: vec![],\n            })\n            .collect(),\n    }\n}\n\n/// Convert a `&[&str]` slice into a column of `PropValue::Str` values.\nfn str_vals(values: &[&str]) -> Vec<PropValue> {\n    values\n        .iter()\n        .map(|s| PropValue::Str(Some((*s).to_string())))\n        .collect()\n}\n\n#[test]\nfn staging_uses_id_presence_analysis() {\n    let all_present = tile_from_ids(&[Some(1), Some(2), Some(3)]);\n    let analysis = all_present.analyze(false).unwrap();\n    let id = analysis.id.as_ref().expect(\"ID analysis\");\n    assert!(id.stats.values_fit_u32());\n    let curve_params = all_present.curve_params();\n\n    let staged = StagedLayer::from_tile(all_present, Unsorted, &analysis, false, curve_params);\n    assert!(matches!(staged.id, StagedId::U32(_)));\n\n    let mixed = tile_from_ids(&[Some(1), None, Some(3)]);\n    let analysis = mixed.analyze(false).unwrap();\n    let id = analysis.id.as_ref().expect(\"ID analysis\");\n    assert!(id.stats.values_fit_u32());\n    let curve_params = mixed.curve_params();\n\n    let staged = StagedLayer::from_tile(mixed, Unsorted, &analysis, false, curve_params);\n    assert!(matches!(staged.id, StagedId::OptU32(_)));\n\n    let large = tile_from_ids(&[Some(u64::from(u32::MAX) + 1), None, Some(3)]);\n    let analysis = large.analyze(false).unwrap();\n    let id = analysis.id.as_ref().expect(\"ID analysis\");\n    assert!(!id.stats.values_fit_u32());\n    let curve_params = large.curve_params();\n\n    let staged = StagedLayer::from_tile(large, Unsorted, &analysis, false, curve_params);\n    assert!(matches!(staged.id, StagedId::OptU64(_)));\n\n    let all_null = tile_from_ids(&[None, None, None]);\n    let analysis = all_null.analyze(false).unwrap();\n    assert_eq!(analysis.id, None);\n    let curve_params = all_null.curve_params();\n\n    let staged = StagedLayer::from_tile(all_null, Unsorted, &analysis, false, curve_params);\n    assert!(matches!(staged.id, StagedId::None));\n}\n\n#[test]\nfn analyze_layer_classifies_id_and_property_presence() {\n    let tile = tile_from_cols_with_ids(\n        &[Some(1), None, Some(3)],\n        &[\n            (\n                \"all_present\",\n                [1u32, 2, 3]\n                    .iter()\n                    .map(|&v| PropValue::U32(Some(v)))\n                    .collect(),\n            ),\n            (\n                \"mixed\",\n                vec![\n                    PropValue::Bool(Some(true)),\n                    PropValue::Bool(None),\n                    PropValue::Bool(Some(false)),\n                ],\n            ),\n            (\n                \"all_null\",\n                vec![\n                    PropValue::Str(None),\n                    PropValue::Str(None),\n                    PropValue::Str(None),\n                ],\n            ),\n        ],\n    );\n\n    let analysis = tile.analyze(true).unwrap();\n\n    let id = analysis.id.as_ref().expect(\"ID analysis\");\n    assert_eq!(id.presence, Presence::SameAsProp(1));\n    assert_eq!(id.stats, PropertyTypedStats::Unsigned { min: 1, max: 3 });\n    assert_eq!(analysis.properties[0].presence, Presence::AllPresent);\n    assert_eq!(\n        analysis.properties[0].stats,\n        PropertyTypedStats::Unsigned { min: 1, max: 3 }\n    );\n    assert_eq!(analysis.properties[1].presence, Presence::Mixed);\n    assert_eq!(analysis.properties[1].stats, PropertyTypedStats::Bool);\n    assert_eq!(analysis.properties[2].presence, Presence::AllNull);\n    assert_eq!(analysis.properties[2].stats, PropertyTypedStats::None);\n}\n\n#[test]\nfn analyze_layer_records_matching_property_presence_as_aliases() {\n    let tile = tile_from_cols_with_ids(\n        &[Some(1), None, Some(3), None],\n        &[\n            (\n                \"a\",\n                vec![\n                    PropValue::U32(Some(1)),\n                    PropValue::U32(None),\n                    PropValue::U32(Some(3)),\n                    PropValue::U32(None),\n                ],\n            ),\n            (\n                \"b\",\n                vec![\n                    PropValue::Bool(Some(true)),\n                    PropValue::Bool(None),\n                    PropValue::Bool(Some(false)),\n                    PropValue::Bool(None),\n                ],\n            ),\n            (\n                \"c\",\n                vec![\n                    PropValue::Str(None),\n                    PropValue::Str(Some(\"x\".to_string())),\n                    PropValue::Str(Some(\"y\".to_string())),\n                    PropValue::Str(None),\n                ],\n            ),\n            (\n                \"d\",\n                vec![\n                    PropValue::I32(Some(1)),\n                    PropValue::I32(Some(2)),\n                    PropValue::I32(Some(3)),\n                    PropValue::I32(Some(4)),\n                ],\n            ),\n            (\n                \"e\",\n                vec![\n                    PropValue::F64(None),\n                    PropValue::F64(Some(1.0)),\n                    PropValue::F64(Some(2.0)),\n                    PropValue::F64(None),\n                ],\n            ),\n            (\n                \"f\",\n                vec![\n                    PropValue::F32(None),\n                    PropValue::F32(None),\n                    PropValue::F32(None),\n                    PropValue::F32(None),\n                ],\n            ),\n        ],\n    );\n\n    let analysis = tile.analyze(false).unwrap();\n\n    assert_eq!(\n        analysis.id.as_ref().expect(\"ID analysis\").presence,\n        Presence::SameAsProp(0)\n    );\n    assert_eq!(analysis.properties[0].presence, Presence::Mixed);\n    assert_eq!(analysis.properties[1].presence, Presence::SameAsProp(0));\n    assert_eq!(analysis.properties[2].presence, Presence::Mixed);\n    assert_eq!(analysis.properties[3].presence, Presence::AllPresent);\n    assert_eq!(analysis.properties[4].presence, Presence::SameAsProp(2));\n    assert_eq!(analysis.properties[5].presence, Presence::AllNull);\n}\n\n#[test]\nfn analyze_layer_tracks_typed_property_stats() {\n    let tile = tile_from_cols(&[\n        (\n            \"small_u64\",\n            vec![\n                PropValue::U64(Some(0)),\n                PropValue::U64(Some(u64::from(u32::MAX))),\n            ],\n        ),\n        (\n            \"large_u64\",\n            vec![\n                PropValue::U64(Some(0)),\n                PropValue::U64(Some(u64::from(u32::MAX) + 1)),\n            ],\n        ),\n        (\n            \"negative_i64\",\n            vec![PropValue::I64(Some(-1)), PropValue::I64(Some(2))],\n        ),\n        (\n            \"names\",\n            vec![\n                PropValue::Str(Some(\"a\".to_string())),\n                PropValue::Str(Some(\"abcd\".to_string())),\n            ],\n        ),\n    ]);\n\n    let analysis = tile.analyze(false).unwrap();\n\n    assert_eq!(\n        analysis.properties[0].stats,\n        PropertyTypedStats::Unsigned {\n            min: 0,\n            max: u64::from(u32::MAX)\n        }\n    );\n    assert!(analysis.properties[0].stats.values_fit_u32());\n    assert_eq!(\n        analysis.properties[1].stats,\n        PropertyTypedStats::Unsigned {\n            min: 0,\n            max: u64::from(u32::MAX) + 1\n        }\n    );\n    assert!(!analysis.properties[1].stats.values_fit_u32());\n    assert_eq!(\n        analysis.properties[2].stats,\n        PropertyTypedStats::Signed { min: -1, max: 2 }\n    );\n    assert!(!analysis.properties[2].stats.values_fit_u32());\n    assert_eq!(\n        analysis.properties[3].stats,\n        PropertyTypedStats::String {\n            shared_dict: SharedDictRole::None\n        }\n    );\n}\n\n#[rstest]\n#[case(vec![PropValue::I8(Some(1)), PropValue::I32(Some(2))])]\n#[case(vec![PropValue::I8(Some(1)), PropValue::I64(Some(2))])]\n#[case(vec![PropValue::I32(Some(1)), PropValue::I64(Some(2))])]\n#[case(vec![PropValue::U8(Some(1)), PropValue::U32(Some(2))])]\n#[case(vec![PropValue::U8(Some(1)), PropValue::U64(Some(2))])]\n#[case(vec![PropValue::U32(Some(1)), PropValue::U64(Some(2))])]\n#[case(vec![PropValue::I8(None), PropValue::I32(Some(2))])]\n#[case(vec![PropValue::I8(Some(1)), PropValue::I64(None)])]\n#[case(vec![PropValue::I32(None), PropValue::I64(Some(2))])]\n#[case(vec![PropValue::U8(None), PropValue::U32(Some(2))])]\n#[case(vec![PropValue::U8(Some(1)), PropValue::U64(None)])]\n#[case(vec![PropValue::U32(None), PropValue::U64(Some(2))])]\n#[case(vec![PropValue::F32(Some(1.0)), PropValue::F64(Some(2.0))])]\n#[case(vec![PropValue::F32(None), PropValue::F64(Some(2.0))])]\n#[case(vec![PropValue::F32(Some(1.0)), PropValue::F64(None)])]\n#[case(vec![PropValue::U32(None), PropValue::Str(Some(\"x\".into()))])]\nfn analyze_layer_rejects_property_type_coercions(#[case] values: Vec<PropValue>) {\n    let tile = tile_from_cols(&[(\"mixed\", values)]);\n    assert!(matches!(\n        tile.analyze(false),\n        Err(MltError::MixedPropertyTypes(0, property_name)) if property_name == \"mixed\"\n    ));\n}\n\n#[rstest]\n#[case(\n    vec![PropValue::Bool(None), PropValue::Bool(Some(true))],\n    PropertyTypedStats::Bool\n)]\n#[case(\n    vec![PropValue::I8(None), PropValue::I8(Some(-1))],\n    PropertyTypedStats::Signed { min: -1, max: -1 }\n)]\n#[case(\n    vec![PropValue::U8(None), PropValue::U8(Some(1))],\n    PropertyTypedStats::Unsigned { min: 1, max: 1 }\n)]\n#[case(\n    vec![PropValue::I32(None), PropValue::I32(Some(-2))],\n    PropertyTypedStats::Signed { min: -2, max: -2 }\n)]\n#[case(\n    vec![PropValue::U32(None), PropValue::U32(Some(2))],\n    PropertyTypedStats::Unsigned { min: 2, max: 2 }\n)]\n#[case(\n    vec![PropValue::I64(None), PropValue::I64(Some(-3))],\n    PropertyTypedStats::Signed { min: -3, max: -3 }\n)]\n#[case(\n    vec![PropValue::U64(None), PropValue::U64(Some(3))],\n    PropertyTypedStats::Unsigned { min: 3, max: 3 }\n)]\n#[case(\n    vec![PropValue::F32(None), PropValue::F32(Some(1.0))],\n    PropertyTypedStats::F32\n)]\n#[case(\n    vec![PropValue::F64(None), PropValue::F64(Some(1.0))],\n    PropertyTypedStats::F64\n)]\n#[case(\n    vec![PropValue::Str(None), PropValue::Str(Some(\"x\".into()))],\n    PropertyTypedStats::String {\n        shared_dict: SharedDictRole::None,\n    }\n)]\nfn analyze_layer_accepts_typed_nulls_matching_column_type(\n    #[case] values: Vec<PropValue>,\n    #[case] expected_stats: PropertyTypedStats,\n) {\n    let tile = tile_from_cols(&[(\"typed_null\", values)]);\n    let analysis = tile.analyze(false).unwrap();\n    assert_eq!(analysis.properties[0].presence, Presence::Mixed);\n    assert_eq!(analysis.properties[0].stats, expected_stats);\n}\n\n#[test]\nfn staging_uses_presence_analysis_for_scalar_variants_and_skips_all_null() {\n    let tile = tile_from_cols(&[\n        (\n            \"all_present\",\n            [1u32, 2, 3]\n                .iter()\n                .map(|&v| PropValue::U32(Some(v)))\n                .collect(),\n        ),\n        (\n            \"mixed\",\n            vec![\n                PropValue::Bool(Some(true)),\n                PropValue::Bool(None),\n                PropValue::Bool(Some(false)),\n            ],\n        ),\n        (\n            \"all_null\",\n            vec![\n                PropValue::Str(None),\n                PropValue::Str(None),\n                PropValue::Str(None),\n            ],\n        ),\n    ]);\n\n    let staged = stage_tile(tile, Unsorted, false, false);\n\n    assert_eq!(staged.properties.len(), 2);\n    assert!(matches!(staged.properties[0], StagedProperty::U32(_)));\n    assert!(matches!(staged.properties[1], StagedProperty::OptBool(_)));\n}\n\n#[test]\nfn analyze_layer_records_shared_dict_roles_by_property_index() {\n    let vocab = &[\"Alice\", \"Bob\", \"Carol\", \"Dave\"];\n    let tile = tile_from_cols(&[(\"name:en\", str_vals(vocab)), (\"name:de\", str_vals(vocab))]);\n\n    let analysis = tile.analyze(true).unwrap();\n\n    let SharedDictRole::Owner(prefix) = analysis.properties[0].stats.shared_dict() else {\n        panic!(\"first string column should own the shared dictionary\");\n    };\n    assert_eq!(prefix, \"name:\");\n    assert_eq!(\n        analysis.properties[1].stats.shared_dict(),\n        SharedDictRole::Member(0)\n    );\n}\n\n#[test]\nfn no_nulls_produces_encoded_output() {\n    let props = vec![StagedProperty::u32(\"pop\", vec![1, 2, 3])];\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    write_properties(&props, &mut enc, &mut codecs).unwrap();\n    assert!(\n        !enc.meta.is_empty(),\n        \"non-null column should write one column\"\n    );\n}\n\n#[test]\nfn all_nulls_encodes_without_error() {\n    let props = vec![StagedProperty::opt_i32(\"x\", vec![None, None, None])];\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    write_properties(&props, &mut enc, &mut codecs).unwrap();\n}\n\n#[test]\nfn sequential_u32_encodes_successfully() {\n    let props = vec![StagedProperty::u32(\"id\", (0u32..1_000).collect())];\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    write_properties(&props, &mut enc, &mut codecs).unwrap();\n    assert!(!enc.meta.is_empty());\n}\n\n#[test]\nfn constant_u32_encodes_successfully() {\n    let props = vec![StagedProperty::u32(\"val\", vec![42u32; 500])];\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    write_properties(&props, &mut enc, &mut codecs).unwrap();\n    assert!(!enc.meta.is_empty());\n}\n\n#[test]\nfn similar_strings_grouped_into_shared_dict() {\n    let vocab = &[\"Alice\", \"Bob\", \"Carol\", \"Dave\"];\n    let tile = tile_from_cols(&[(\"name:en\", str_vals(vocab)), (\"name:de\", str_vals(vocab))]);\n\n    let res = tile.analyze(true).unwrap();\n    assert_eq!(\n        res.properties[0].stats.shared_dict(),\n        SharedDictRole::Owner(\"name:\".to_string())\n    );\n    assert_eq!(\n        res.properties[1].stats.shared_dict(),\n        SharedDictRole::Member(0),\n        \"second similar string column should join the first column's SharedDict\"\n    );\n}\n\n#[test]\nfn multiple_similar_string_columns_grouped() {\n    let vocab = &[\"alpha\", \"beta\", \"gamma\", \"delta\"];\n    let tile = tile_from_cols(&[\n        (\"addr:zip\", str_vals(vocab)),\n        (\"addr:street\", str_vals(vocab)),\n        (\"addr:zipcode\", str_vals(vocab)),\n    ]);\n\n    let res = tile.analyze(true).unwrap();\n    assert_eq!(\n        res.properties[0].stats.shared_dict(),\n        SharedDictRole::Owner(\"addr:\".to_string())\n    );\n    assert_eq!(\n        res.properties[1].stats.shared_dict(),\n        SharedDictRole::Member(0)\n    );\n    assert_eq!(\n        res.properties[2].stats.shared_dict(),\n        SharedDictRole::Member(0),\n        \"all similar string columns should join the first column's SharedDict\"\n    );\n}\n\n#[test]\nfn dissimilar_strings_stay_scalar() {\n    let tile = tile_from_cols(&[\n        (\"city:de\", str_vals(&[\"Munich\", \"Manheim\", \"Garching\"])),\n        (\"city:colourado\", str_vals(&[\"Black\", \"Red\", \"Gold\"])),\n    ]);\n\n    let res = tile.analyze(true).unwrap();\n\n    assert_eq!(res.properties[0].stats.shared_dict(), SharedDictRole::None);\n    assert_eq!(res.properties[1].stats.shared_dict(), SharedDictRole::None);\n}\n\n#[test]\nfn mixed_scalars_and_grouped_strings() {\n    let vocab = &[\"alpha\", \"beta\", \"gamma\"];\n    let tile = tile_from_cols(&[\n        (\"id\", (1u32..=3).map(|v| PropValue::U32(Some(v))).collect()),\n        (\"name:en\", str_vals(vocab)),\n        (\"name:de\", str_vals(vocab)),\n        (\n            \"count\",\n            [10i32, 20, 30]\n                .iter()\n                .map(|&v| PropValue::I32(Some(v)))\n                .collect(),\n        ),\n    ]);\n\n    let res = tile.analyze(true).unwrap();\n\n    assert_eq!(res.properties[0].stats.shared_dict(), SharedDictRole::None);\n    assert_eq!(\n        res.properties[1].stats.shared_dict(),\n        SharedDictRole::Owner(\"name:\".to_string())\n    );\n    assert_eq!(\n        res.properties[2].stats.shared_dict(),\n        SharedDictRole::Member(1)\n    );\n    assert_eq!(res.properties[3].stats.shared_dict(), SharedDictRole::None);\n}\n\n#[test]\nfn encode_with_explicit_encoder_works() {\n    let props = vec![StagedProperty::u32(\"id\", (1_000u32..2_000).collect())];\n    let mut enc = Encoder::default();\n    let mut codecs = Codecs::default();\n    write_properties(&props, &mut enc, &mut codecs).unwrap();\n    assert!(!enc.meta.is_empty());\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/sort.rs",
    "content": "//! Feature reordering for the optimizer\n\nuse geo::CoordsIter as _;\nuse geo_types::{Coord, Geometry};\n\nuse crate::codecs::hilbert::{hilbert_curve_params_from_bounds, hilbert_sort_key};\nuse crate::codecs::morton::morton_sort_key;\nuse crate::decoder::TileLayer;\nuse crate::encoder::model::CurveParams;\n\n/// Controls how features inside a layer are reordered before encoding.\n///\n/// Reordering features changes their position in every parallel column\n/// (geometry, ID, and all properties simultaneously), so the caller must\n/// opt in explicitly.\n#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, strum::EnumIter, strum::EnumCount)]\npub enum SortStrategy {\n    /// Preserve the original feature order — no reordering is applied.\n    ///\n    /// This is the default.\n    #[default]\n    Unsorted,\n\n    /// Sort features by the Z-order (Morton) curve index of their first vertex.\n    ///\n    /// Fast to compute.  Spatially close features end up adjacent in the\n    /// stream, improving RLE run lengths for location-correlated properties\n    /// and CPU cache locality during client-side decoding.\n    ///\n    SpatialMorton,\n\n    /// Sort features by the Hilbert curve index of their first vertex.\n    ///\n    /// Slower to compute than Morton but achieves superior spatial locality.\n    SpatialHilbert,\n\n    /// Sort features by their feature ID in ascending order.\n    Id,\n}\n\nimpl TileLayer {\n    /// Reorder features by `strategy`, using `params` as the curve normalization\n    /// for [`SortStrategy::SpatialMorton`] / [`SortStrategy::SpatialHilbert`].\n    ///\n    /// `params` is taken as a parameter (rather than recomputed here) so the\n    /// same scan feeds the encoder's dictionary builders, see\n    /// [`TileLayer::curve_params`].\n    ///\n    /// [`SortStrategy::Unsorted`] is a no-op; layers with ≤1 feature are\n    /// trivially unchanged.\n    #[hotpath::measure]\n    pub fn sort(&mut self, strategy: SortStrategy, params: CurveParams) {\n        match strategy {\n            SortStrategy::SpatialMorton | SortStrategy::SpatialHilbert => {\n                let curve_key = if let SortStrategy::SpatialMorton = strategy {\n                    morton_sort_key\n                } else {\n                    hilbert_sort_key\n                };\n                self.features.sort_by_cached_key(|f| {\n                    first_vertex(&f.geometry).map_or(u64::MAX, |c| u64::from(curve_key(c, params)))\n                });\n            }\n            SortStrategy::Id => {\n                self.features\n                    .sort_by_cached_key(|f| f.id.map_or(0, |v| v.saturating_add(1)));\n            }\n            SortStrategy::Unsorted => {\n                // do nothing\n            }\n        }\n    }\n\n    /// Compute Hilbert/Morton [`CurveParams`] for this layer.\n    ///\n    /// The bounds are order-invariant, so the optimizer calls this once per\n    /// layer and reuses the result across every sort trial and the encoder's\n    /// dictionary builders.\n    #[hotpath::measure]\n    #[must_use]\n    pub fn curve_params(&self) -> CurveParams {\n        let (min_val, max_val) = self\n            .features\n            .iter()\n            .flat_map(|f| f.geometry.coords_iter())\n            .fold((i32::MAX, i32::MIN), |(min, max), c| {\n                (min.min(c.x).min(c.y), max.max(c.x).max(c.y))\n            });\n        hilbert_curve_params_from_bounds(min_val, max_val)\n    }\n}\n\n/// Extract the coordinate of the first vertex of a geometry.\nfn first_vertex(geom: &Geometry<i32>) -> Option<Coord<i32>> {\n    match geom {\n        Geometry::<i32>::Point(p) => Some(p.0),\n        Geometry::<i32>::Line(l) => Some(l.start),\n        Geometry::<i32>::LineString(ls) => ls.0.first().copied(),\n        Geometry::<i32>::Polygon(p) => p.exterior().0.first().copied(),\n        Geometry::<i32>::MultiPoint(mp) => mp.0.first().map(|p| p.0),\n        Geometry::<i32>::MultiLineString(mls) => mls.0.first().and_then(|ls| ls.0.first().copied()),\n        Geometry::<i32>::MultiPolygon(mp) => {\n            mp.0.first().and_then(|p| p.exterior().0.first().copied())\n        }\n        Geometry::<i32>::Triangle(t) => Some(t.v1()),\n        Geometry::<i32>::Rect(r) => Some(r.min()),\n        Geometry::<i32>::GeometryCollection(gc) => gc.0.first().and_then(first_vertex),\n    }\n}\n\n/// Return `true` if a spatial sort is likely to reduce compressed size.\n///\n/// The heuristic: if the vertex bounding box spans more than\n/// `SPATIAL_HELP_COVERAGE` of the layer's tile extent on **both** axes, the\n/// features are too spread-out for locality clustering to help, so spatial\n/// sorting is skipped.\npub(crate) fn spatial_sort_likely_to_help(layer: &TileLayer) -> bool {\n    const SPATIAL_HELP_COVERAGE: f64 = 0.8;\n\n    let extent = f64::from(layer.extent);\n    if extent <= 0.0 || layer.features.is_empty() {\n        return true;\n    }\n\n    let (min_x, max_x, min_y, max_y) = layer\n        .features\n        .iter()\n        .filter_map(|f| first_vertex(&f.geometry))\n        .fold(\n            (i32::MAX, i32::MIN, i32::MAX, i32::MIN),\n            |(min_x, max_x, min_y, max_y), Coord::<i32> { x, y }| {\n                (min_x.min(x), max_x.max(x), min_y.min(y), max_y.max(y))\n            },\n        );\n\n    if min_x > max_x || min_y > max_y {\n        return true;\n    }\n\n    let range_x = f64::from(max_x - min_x);\n    let range_y = f64::from(max_y - min_y);\n\n    let spread_x = range_x / extent;\n    let spread_y = range_y / extent;\n\n    !(spread_x > SPATIAL_HELP_COVERAGE && spread_y > SPATIAL_HELP_COVERAGE)\n}\n\n#[cfg(test)]\nmod tests {\n    use geo_types::{Coord, Geometry as GeoGeom, Geometry, LineString, Point, Polygon};\n\n    use crate::decoder::{GeometryType, GeometryValues, RawGeometry, TileFeature, TileLayer};\n    use crate::encoder::{Codecs, Encoder, ExplicitEncoder, IntEncoder, SortStrategy, stage_tile};\n    use crate::test_helpers::{assert_empty, dec, into_layer01, parser};\n    use crate::{Layer, LazyParsed};\n\n    fn pt(x: i32, y: i32) -> Geometry<i32> {\n        GeoGeom::Point(Point::new(x, y))\n    }\n\n    fn ls(coords: &[(i32, i32)]) -> Geometry<i32> {\n        GeoGeom::LineString(LineString::new(\n            coords.iter().map(|&(x, y)| Coord { x, y }).collect(),\n        ))\n    }\n\n    fn poly_square(x0: i32, y0: i32, side: i32) -> Geometry<i32> {\n        let ring = LineString::new(vec![\n            Coord { x: x0, y: y0 },\n            Coord {\n                x: x0 + side,\n                y: y0,\n            },\n            Coord {\n                x: x0 + side,\n                y: y0 + side,\n            },\n            Coord {\n                x: x0,\n                y: y0 + side,\n            },\n            Coord { x: x0, y: y0 },\n        ]);\n        GeoGeom::Polygon(Polygon::new(ring, vec![]))\n    }\n\n    /// Encode + serialize + parse + decode a `GeometryValues` (round-trip).\n    fn roundtrip_geom(decoded: &GeometryValues) -> GeometryValues {\n        let mut enc = Encoder::default();\n        let mut codecs = Codecs::default();\n        decoded\n            .clone()\n            .write_to(&mut enc, &mut codecs)\n            .expect(\"encode failed\");\n        let buf = enc.data;\n\n        let parsed = assert_empty(RawGeometry::from_bytes(&buf, &mut parser()));\n        let mut d = dec();\n        let result = LazyParsed::Raw(parsed)\n            .into_parsed(&mut d)\n            .expect(\"decode failed\");\n        assert!(\n            d.consumed() > 0,\n            \"decoder should consume bytes after decode\"\n        );\n        result\n    }\n\n    /// Build the canonical (dense, wire-decoded) form of an ordered geometry sequence.\n    fn canonical(geoms: &[Geometry<i32>]) -> GeometryValues {\n        let mut decoded = GeometryValues::default();\n        for g in geoms {\n            decoded.push_geom(g);\n        }\n        roundtrip_geom(&decoded)\n    }\n\n    /// Build a `TileLayer` from `geoms` and `ids`, apply `reorder_features`,\n    /// and return it.\n    fn layer_after_sort(geoms: &[Geometry<i32>], ids: &[u64], strategy: SortStrategy) -> TileLayer {\n        let features: Vec<TileFeature> = geoms\n            .iter()\n            .zip(ids.iter())\n            .map(|(g, &id)| TileFeature {\n                id: Some(id),\n                geometry: g.clone(),\n                properties: vec![],\n            })\n            .collect();\n\n        let mut layer = TileLayer {\n            name: \"test\".to_string(),\n            extent: 4096,\n            property_names: vec![],\n            features,\n        };\n\n        let params = layer.curve_params();\n        layer.sort(strategy, params);\n        layer\n    }\n\n    /// Sort, then encode+decode the result and compare to `canonical(expected)`.\n    fn assert_sort_roundtrip(\n        geoms: &[Geometry<i32>],\n        ids: &[u64],\n        strategy: SortStrategy,\n        expected: &[Geometry<i32>],\n    ) {\n        let layer = layer_after_sort(geoms, ids, strategy);\n\n        let mut sorted_decoded = GeometryValues::default();\n        for f in &layer.features {\n            sorted_decoded.push_geom(&f.geometry);\n        }\n\n        let after_roundtrip = roundtrip_geom(&sorted_decoded);\n        let expected_canonical = canonical(expected);\n\n        assert_eq!(\n            after_roundtrip, expected_canonical,\n            \"\\nsorted geometry did not match expected after encode→decode round-trip\\\n             \\nvector_types after sort: {:?}\\\n             \\nvector_types expected:   {:?}\",\n            sorted_decoded.vector_types, expected_canonical.vector_types,\n        );\n    }\n\n    // ── pure Points ──────────────────────────────────────────────────────────\n\n    #[test]\n    fn pure_points_id_sort_roundtrip() {\n        assert_sort_roundtrip(\n            &[pt(0, 0), pt(1, 1), pt(2, 2)],\n            &[3, 2, 1],\n            SortStrategy::Id,\n            &[pt(2, 2), pt(1, 1), pt(0, 0)],\n        );\n    }\n\n    // ── pure LineStrings ─────────────────────────────────────────────────────\n\n    #[test]\n    fn pure_linestrings_id_sort_roundtrip() {\n        assert_sort_roundtrip(\n            &[ls(&[(0, 0), (0, 10)]), ls(&[(5, 5), (10, 10)])],\n            &[2, 1],\n            SortStrategy::Id,\n            &[ls(&[(5, 5), (10, 10)]), ls(&[(0, 0), (0, 10)])],\n        );\n    }\n\n    // ── [Point, LineString, Point] ────────────────────────────────────────────\n\n    #[test]\n    fn point_line_point_id_sort_to_line_point_point_roundtrip() {\n        assert_sort_roundtrip(\n            &[pt(0, 0), ls(&[(1, 0), (1, 5)]), pt(5, 5)],\n            &[3, 1, 2],\n            SortStrategy::Id,\n            &[ls(&[(1, 0), (1, 5)]), pt(5, 5), pt(0, 0)],\n        );\n    }\n\n    #[test]\n    fn point_line_point_id_sort_to_point_point_line_roundtrip() {\n        assert_sort_roundtrip(\n            &[pt(0, 0), ls(&[(1, 0), (1, 5)]), pt(5, 5)],\n            &[1, 3, 2],\n            SortStrategy::Id,\n            &[pt(0, 0), pt(5, 5), ls(&[(1, 0), (1, 5)])],\n        );\n    }\n\n    // ── [Point, Polygon, Point] ───────────────────────────────────────────────\n\n    #[test]\n    fn point_polygon_point_id_sort_roundtrip() {\n        assert_sort_roundtrip(\n            &[pt(0, 0), poly_square(10, 10, 5), pt(5, 5)],\n            &[2, 1, 3],\n            SortStrategy::Id,\n            &[poly_square(10, 10, 5), pt(0, 0), pt(5, 5)],\n        );\n    }\n\n    // ── spatial Morton sort ───────────────────────────────────────────────────\n\n    #[test]\n    fn point_line_point_morton_sort_roundtrip() {\n        assert_sort_roundtrip(\n            &[pt(2, 0), ls(&[(0, 0), (0, 5)]), pt(1, 0)],\n            &[1, 2, 3],\n            SortStrategy::SpatialMorton,\n            &[ls(&[(0, 0), (0, 5)]), pt(1, 0), pt(2, 0)],\n        );\n    }\n\n    // ── already-sorted is identity ────────────────────────────────────────────\n\n    #[test]\n    fn id_sort_already_sorted_is_identity_roundtrip() {\n        let geoms = &[pt(0, 0), ls(&[(1, 0), (1, 5)]), pt(5, 5)];\n        assert_sort_roundtrip(geoms, &[1, 2, 3], SortStrategy::Id, geoms);\n    }\n\n    // ── ID column co-permuted with geometry ───────────────────────────────────\n\n    #[test]\n    fn id_column_co_permuted_with_geometry() {\n        let layer = layer_after_sort(\n            &[pt(0, 0), ls(&[(1, 0), (1, 5)]), pt(5, 5)],\n            &[3, 1, 2],\n            SortStrategy::Id,\n        );\n\n        let ids: Vec<Option<u64>> = layer.features.iter().map(|f| f.id).collect();\n        assert_eq!(ids, vec![Some(1u64), Some(2), Some(3)]);\n\n        // Verify geometry types match expected order\n        let geom_types: Vec<&str> = layer\n            .features\n            .iter()\n            .map(|f| GeometryType::try_from(&f.geometry).unwrap().into())\n            .collect();\n        assert_eq!(geom_types, vec![\"LineString\", \"Point\", \"Point\"]);\n    }\n\n    /// Build row-oriented tile layer from geometries and IDs (one feature per geometry).\n    fn build_tile_layer(geoms: &[Geometry<i32>], ids: &[Option<u64>]) -> TileLayer {\n        assert_eq!(geoms.len(), ids.len());\n        TileLayer {\n            name: \"test\".to_string(),\n            extent: 4096,\n            property_names: vec![],\n            features: geoms\n                .iter()\n                .zip(ids.iter())\n                .map(|(g, &id)| TileFeature {\n                    id,\n                    geometry: g.clone(),\n                    properties: vec![],\n                })\n                .collect(),\n        }\n    }\n\n    /// Encode the layer with a given sort strategy, decode it back, and return the `TileLayer`.\n    /// This tests the full encode→decode roundtrip, verifying that sorting was applied.\n    fn sort_encode_decode(tile: TileLayer, sort: SortStrategy) -> TileLayer {\n        let enc_cfg = Encoder::default().cfg;\n        let enc = Encoder::with_explicit(enc_cfg, ExplicitEncoder::for_id(IntEncoder::varint()));\n        let mut codecs = Codecs::default();\n        let enc = stage_tile(tile, sort, false, enc_cfg.tessellate)\n            .encode_into(enc, &mut codecs)\n            .expect(\"encode failed\");\n\n        // Serialize to bytes and reparse to get a `Layer01`.\n        let buf = enc.into_layer_bytes().expect(\"into_layer_bytes failed\");\n\n        let mut p = parser();\n        let layer_back = assert_empty(Layer::from_bytes(&buf, &mut p));\n        assert!(p.reserved() > 0, \"parser should reserve bytes after parse\");\n\n        let layer01 = into_layer01(layer_back);\n\n        let mut d = dec();\n        let tile = layer01.into_tile(&mut d).expect(\"decode after sort failed\");\n        assert!(\n            d.consumed() > 0,\n            \"decoder should consume bytes after decode\"\n        );\n        tile\n    }\n\n    /// Rebuild a flat vertex buffer from the feature geometries in source order.\n    fn vertices_from_source(source: &TileLayer) -> Vec<i32> {\n        let mut geom = GeometryValues::default();\n        for f in &source.features {\n            geom.push_geom(&f.geometry);\n        }\n        geom.vertices().unwrap_or_default().to_vec()\n    }\n\n    #[test]\n    fn test_shared_morton_shift() {\n        // P1 at (0, -10), P2 at (-10, 0).\n        // With shared shift = 10:\n        // P1 shifted: (10, 0) -> interleave(10, 0) = 68\n        // P2 shifted: (0, 10) -> interleave(0, 10) = 136\n        // P1 (key 68) < P2 (key 136), so expected order: [P1(0,-10), P2(-10,0)].\n\n        let tile = build_tile_layer(&[pt(0, -10), pt(-10, 0)], &[Some(1), Some(2)]);\n        let source = sort_encode_decode(tile, SortStrategy::SpatialMorton);\n\n        let verts = vertices_from_source(&source);\n        assert_eq!(verts, vec![0, -10, -10, 0]);\n    }\n\n    #[test]\n    fn test_id_sort_nulls_first() {\n        let tile = build_tile_layer(&[pt(2, 2), pt(1, 1), pt(0, 0)], &[Some(10), None, Some(5)]);\n        let source = sort_encode_decode(tile, SortStrategy::Id);\n\n        let ids: Vec<Option<u64>> = source.features.iter().map(|f| f.id).collect();\n        // Expected order: [None, Some(5), Some(10)]\n        assert_eq!(ids, vec![None, Some(5), Some(10)]);\n\n        let verts = vertices_from_source(&source);\n        // Corresponding verts: [pt(1,1), pt(0,0), pt(2,2)] -> [1,1, 0,0, 2,2]\n        assert_eq!(verts, vec![1, 1, 0, 0, 2, 2]);\n    }\n\n    #[test]\n    fn test_mixed_geometry_morton_sort() {\n        // [Point(2,0), LineString(0,0 -> 0,5), Point(1,0)]\n        // Morton keys (assuming shift 0):\n        // P1(2,0) -> 4\n        // LS(0,0) -> 0\n        // P2(1,0) -> 1\n        // Expected order: [LS, P2, P1]\n\n        let tile = build_tile_layer(\n            &[pt(2, 0), ls(&[(0, 0), (0, 5)]), pt(1, 0)],\n            &[Some(1), Some(2), Some(3)],\n        );\n        let source = sort_encode_decode(tile, SortStrategy::SpatialMorton);\n\n        let types: Vec<_> = source\n            .features\n            .iter()\n            .map(|f| GeometryType::try_from(&f.geometry).unwrap())\n            .collect();\n\n        assert_eq!(\n            types,\n            vec![\n                GeometryType::LineString,\n                GeometryType::Point,\n                GeometryType::Point\n            ]\n        );\n\n        let verts = vertices_from_source(&source);\n        // Expected vertices: LS(0,0,0,5), P2(1,0), P1(2,0)\n        assert_eq!(verts, vec![0, 0, 0, 5, 1, 0, 2, 0]);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/codecs.rs",
    "content": "use std::collections::HashMap;\n\nuse bytemuck::{NoUninit, cast_slice};\nuse fastpfor::FastPFor256;\nuse num_traits::WrappingSub;\nuse zigzag::ZigZag;\n\nuse crate::codecs::bytes::encode_bools_to_bytes;\nuse crate::codecs::rle::encode_byte_rle;\nuse crate::decoder::{LogicalEncoding, PhysicalEncoding, RleMeta, StreamMeta, StreamType};\nuse crate::encoder;\nuse crate::encoder::Encoder;\nuse crate::encoder::model::StreamCtx;\nuse crate::encoder::stream::logical::LogicalEncoder;\nuse crate::encoder::stream::optimizer::DataProfile;\nuse crate::encoder::write::{LogicalIntCodec, LogicalIntStreamKind, PhysicalIntStreamKind};\nuse crate::errors::MltResult;\n\n#[derive(Default)]\npub struct Codecs {\n    pub(crate) logical: LogicalCodecs,\n    pub(crate) physical: PhysicalCodecs,\n}\n\n#[derive(Default)]\npub struct PhysicalCodecs {\n    pub(crate) u32_tmp: Vec<u32>,\n    pub(crate) u8_tmp: Vec<u8>,\n    pub(crate) fastpfor: FastPFor256,\n}\n\n#[derive(Default)]\npub struct LogicalCodecs {\n    pub(crate) u32_tmp: Vec<u32>,\n    pub(crate) u32_tmp2: Vec<u32>,\n    pub(crate) u64_tmp: Vec<u64>,\n    pub(crate) u64_tmp2: Vec<u64>,\n    u8_tmp: Vec<u8>,\n    u8_tmp2: Vec<u8>,\n\n    /// Reusable scratch for the Hilbert vertex-dictionary builder. The four\n    /// slots are taken out via `mem::take` for the duration of a build so the\n    /// caller can hold a `&[..]` view into one slot while passing `&mut Codecs`\n    /// to a stream writer; capacity is preserved across geometry columns.\n    pub(crate) hilbert_offsets: Vec<u32>,\n    pub(crate) hilbert_indexed: Vec<u64>,\n    pub(crate) hilbert_dict_xy: Vec<i32>,\n    pub(crate) hilbert_remap: HashMap<u32, u32>,\n}\n\nimpl LogicalCodecs {\n    #[hotpath::measure]\n    pub(crate) fn encode_bools(\n        &mut self,\n        values: impl ExactSizeIterator<Item = bool>,\n    ) -> MltResult<(LogicalEncoding, &[u8])> {\n        let num_values = u32::try_from(values.len())?;\n        let data = encode_bools_to_bytes(values, &mut self.u8_tmp);\n        let encoded = encode_byte_rle(data, &mut self.u8_tmp2);\n        let meta = LogicalEncoding::Rle(RleMeta {\n            runs: num_values.div_ceil(8),\n            num_rle_values: u32::try_from(encoded.len())?,\n        });\n        Ok((meta, encoded))\n    }\n}\n\nimpl Codecs {\n    pub(crate) fn write_bool_stream(\n        &mut self,\n        values: impl ExactSizeIterator<Item = bool>,\n        stream_type: StreamType,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        let num_values = values.len();\n        let (logical, vals) = self.logical.encode_bools(values)?;\n        let meta = StreamMeta::new2(stream_type, logical, PhysicalEncoding::None, num_values)?;\n        encoder::write_stream_payload(&mut enc.data, meta, true, vals)\n    }\n\n    pub(crate) fn write_presence_stream(\n        &mut self,\n        values: impl ExactSizeIterator<Item = bool>,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        self.write_bool_stream(values, StreamType::Present, enc)\n    }\n\n    #[expect(\n        clippy::unused_self,\n        reason = \"kept as a Codecs method to match the other stream writers\"\n    )]\n    pub(crate) fn write_float_stream<T: NoUninit>(\n        &mut self,\n        values: &[T],\n        stream_type: StreamType,\n        enc: &mut Encoder,\n    ) -> MltResult<()> {\n        #[cfg(not(target_endian = \"little\"))]\n        compile_error!(\"not implemented for non-little-endian targets\");\n\n        let meta = StreamMeta::new_none(stream_type, values.len())?;\n        encoder::write_stream_payload(&mut enc.data, meta, false, cast_slice(values))\n    }\n\n    pub(crate) fn write_int_stream<T>(\n        &mut self,\n        values: &[T],\n        ctx: &StreamCtx<'_>,\n        enc: &mut Encoder,\n    ) -> MltResult<()>\n    where\n        [T]: LogicalIntStreamKind<Input = T>,\n        <<[T] as LogicalIntStreamKind>::Profile as ZigZag>::UInt: WrappingSub,\n        LogicalCodecs: LogicalIntCodec<[T]>,\n    {\n        type Output<T> = <[T] as LogicalIntStreamKind>::Output;\n\n        use LogicalEncoding as LE;\n        use PhysicalEncoding as PE;\n\n        // FIXME: does StreamMeta encode values.len() or vals1.len()?\n        if let Some(int_enc) = enc.override_int_enc(ctx) {\n            let (le, vals) = match int_enc.logical {\n                LogicalEncoder::None => (LE::None, self.logical.none(values)),\n                LogicalEncoder::Delta => (LE::Delta, self.logical.delta(values)),\n                LogicalEncoder::Rle => self.logical.rle(values)?,\n                LogicalEncoder::DeltaRle => self.logical.delta_rle(values)?,\n            };\n            let phys = int_enc.physical;\n            return self\n                .physical\n                .write_encoded_as::<Output<T>>(ctx, enc, le, vals, phys);\n        }\n\n        if values.is_empty() {\n            let vals1 = self.logical.none(values);\n            let vals2 = Output::<T>::none(&mut self.physical, vals1);\n            let meta = StreamMeta::new2(ctx.stream_type, LE::None, PE::None, vals1.len())?;\n            return encoder::write_stream_payload(&mut enc.data, meta, false, vals2);\n        }\n\n        let Self { logical, physical } = self;\n        let mut alt = enc.try_alternatives();\n\n        let sample = logical.none(DataProfile::take_sample(values));\n        let profile = DataProfile::profile::<<[T] as LogicalIntStreamKind>::Profile>(sample);\n\n        if profile.delta_is_beneficial()\n            && (profile.rle_is_viable() || profile.delta_rle_is_viable())\n        {\n            let (logical_enc, values) = logical.delta_rle(values)?;\n            physical.write_alternatives::<Output<T>>(\n                &mut alt,\n                values,\n                logical_enc,\n                ctx.stream_type,\n            )?;\n        }\n        if profile.delta_is_beneficial() {\n            let values = logical.delta(values);\n            physical.write_alternatives::<Output<T>>(\n                &mut alt,\n                values,\n                LE::Delta,\n                ctx.stream_type,\n            )?;\n        }\n        if profile.rle_is_viable() {\n            let (logical_enc, values) = logical.rle(values)?;\n            physical.write_alternatives::<Output<T>>(\n                &mut alt,\n                values,\n                logical_enc,\n                ctx.stream_type,\n            )?;\n        }\n        let values = logical.none(values);\n        physical.write_alternatives::<Output<T>>(&mut alt, values, LE::None, ctx.stream_type)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/encode_stream.rs",
    "content": "use std::collections::HashMap;\nuse std::collections::hash_map::Entry;\n\nuse crate::MltResult;\n#[cfg(any(test, feature = \"__private\"))]\nuse crate::decoder::{DictionaryType, StreamMeta, StreamType};\n#[cfg(any(test, feature = \"__private\"))]\nuse crate::encoder::EncodedStream;\nuse crate::errors::AsMltError as _;\n\n/// Deduplicate `values` preserving insertion order.\n/// Returns `(unique_strings, per_value_index)` where each entry in `per_value_index` is the\n/// index into `unique_strings` for the corresponding input value.\npub(crate) fn dedup_strings<S: AsRef<str>>(values: &[S]) -> MltResult<(Vec<&str>, Vec<u32>)> {\n    let mut unique: Vec<&str> = Vec::new();\n    let mut index: HashMap<&str, u32> = HashMap::new();\n    let mut indices = Vec::with_capacity(values.len());\n    for value in values {\n        let s = value.as_ref();\n        let idx = match index.entry(s) {\n            Entry::Occupied(e) => *e.get(),\n            Entry::Vacant(e) => {\n                let idx = u32::try_from(unique.len()).or_overflow()?;\n                unique.push(s);\n                *e.insert(idx)\n            }\n        };\n        indices.push(idx);\n    }\n    Ok((unique, indices))\n}\n\n#[cfg(any(test, feature = \"__private\"))]\nimpl EncodedStream {\n    /// Encodes `f32`s into a stream\n    #[hotpath::measure]\n    pub fn encode_f32(values: &[f32]) -> MltResult<Self> {\n        let data = values\n            .iter()\n            .flat_map(|f| f.to_le_bytes())\n            .collect::<Vec<u8>>();\n        let meta = StreamMeta::new_none(StreamType::Data(DictionaryType::None), values.len())?;\n        Ok(Self { meta, data })\n    }\n\n    /// Encodes `f64`s into a stream\n    #[hotpath::measure]\n    pub fn encode_f64(values: &[f64]) -> MltResult<Self> {\n        let data = values\n            .iter()\n            .flat_map(|v| v.to_le_bytes())\n            .collect::<Vec<u8>>();\n        let meta = StreamMeta::new_none(StreamType::Data(DictionaryType::None), values.len())?;\n        Ok(Self { meta, data })\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/encoder.rs",
    "content": "use crate::encoder::stream::logical::LogicalEncoder;\nuse crate::encoder::stream::physical::PhysicalEncoder;\n\n#[derive(Debug, Eq, PartialEq, Clone, Copy)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub struct IntEncoder {\n    pub(crate) logical: LogicalEncoder,\n    pub(crate) physical: PhysicalEncoder,\n}\n\nimpl IntEncoder {\n    #[must_use]\n    pub const fn new(logical: LogicalEncoder, physical: PhysicalEncoder) -> Self {\n        Self { logical, physical }\n    }\n\n    #[must_use]\n    pub fn delta_fastpfor() -> Self {\n        Self::new(LogicalEncoder::Delta, PhysicalEncoder::FastPFOR)\n    }\n    #[must_use]\n    pub fn delta_rle_fastpfor() -> Self {\n        Self::new(LogicalEncoder::DeltaRle, PhysicalEncoder::FastPFOR)\n    }\n    #[must_use]\n    pub fn delta_rle_varint() -> Self {\n        Self::new(LogicalEncoder::DeltaRle, PhysicalEncoder::VarInt)\n    }\n    #[must_use]\n    pub fn delta_varint() -> Self {\n        Self::new(LogicalEncoder::Delta, PhysicalEncoder::VarInt)\n    }\n    #[must_use]\n    pub fn fastpfor() -> Self {\n        Self::new(LogicalEncoder::None, PhysicalEncoder::FastPFOR)\n    }\n    #[must_use]\n    pub fn plain() -> Self {\n        Self::new(LogicalEncoder::None, PhysicalEncoder::None)\n    }\n    #[must_use]\n    pub fn rle_fastpfor() -> Self {\n        Self::new(LogicalEncoder::Rle, PhysicalEncoder::FastPFOR)\n    }\n    #[must_use]\n    pub fn rle_varint() -> Self {\n        Self::new(LogicalEncoder::Rle, PhysicalEncoder::VarInt)\n    }\n    #[must_use]\n    pub fn varint() -> Self {\n        Self::new(LogicalEncoder::None, PhysicalEncoder::VarInt)\n    }\n    #[must_use]\n    pub fn varint_with(logical: LogicalEncoder) -> Self {\n        Self::new(logical, PhysicalEncoder::VarInt)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/logical.rs",
    "content": "use std::fmt::Debug;\n\nuse num_traits::PrimInt;\n\nuse crate::MltResult;\nuse crate::codecs::rle::encode_rle;\nuse crate::decoder::RleMeta;\n\n/// RLE-encode `data` into `target` and return the matching `RleMeta`.\n///\n/// `target` is treated as a scratch buffer: cleared before writing.\n/// `num_logical` is the expanded output length (stored in `RleMeta::num_rle_values`).\npub(crate) fn apply_rle<T: PrimInt + Debug>(\n    data: &[T],\n    num_logical: usize,\n    target: &mut Vec<T>,\n) -> MltResult<RleMeta> {\n    let (runs_vec, vals_vec) = encode_rle(data);\n    let meta = RleMeta {\n        runs: u32::try_from(runs_vec.len())?,\n        num_rle_values: u32::try_from(num_logical)?,\n    };\n    target.clear();\n    target.extend_from_slice(&runs_vec);\n    target.extend_from_slice(&vals_vec);\n    Ok(meta)\n}\n\n#[derive(Debug, Eq, PartialEq, Clone, Copy, Default, strum::EnumIter)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub enum LogicalEncoder {\n    #[default]\n    None,\n    Delta,\n    DeltaRle,\n    Rle,\n    // FIXME: add more of the LogicalEncoding strategies\n}\n\n#[cfg(test)]\nmod tests {\n    use proptest::prelude::*;\n\n    use super::*;\n    use crate::decoder::RawStream;\n    use crate::encoder::model::StreamCtx;\n    use crate::encoder::{Codecs, Encoder, ExplicitEncoder, IntEncoder};\n    use crate::test_helpers::{assert_empty, dec, parser};\n\n    proptest! {\n        #[test]\n        fn test_u32_logical_roundtrip(\n            values in prop::collection::vec(any::<u32>(), 0..100),\n            logical in any::<LogicalEncoder>(),\n        ) {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint_with(logical)),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop_data(\"test\");\n            codecs.write_int_stream(&values, &ctx, &mut enc).unwrap();\n            let parsed = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n            let decoded = parsed.decode_u32s(&mut dec()).unwrap();\n            prop_assert_eq!(decoded, values);\n        }\n\n        #[test]\n        fn test_i32_logical_roundtrip(\n            values in prop::collection::vec(any::<i32>(), 0..100),\n            logical in any::<LogicalEncoder>(),\n        ) {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint_with(logical)),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop_data(\"test\");\n            codecs.write_int_stream(&values, &ctx, &mut enc).unwrap();\n            let parsed = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n            let decoded = parsed.decode_i32s(&mut dec()).unwrap();\n            prop_assert_eq!(decoded, values);\n        }\n\n        #[test]\n        fn test_u64_logical_roundtrip(\n            values in prop::collection::vec(any::<u64>(), 0..100),\n            logical in any::<LogicalEncoder>(),\n        ) {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint_with(logical)),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop_data(\"test\");\n            codecs.write_int_stream(&values, &ctx, &mut enc).unwrap();\n            let parsed = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n            let decoded = parsed.decode_u64s(&mut dec()).unwrap();\n            prop_assert_eq!(decoded, values);\n        }\n\n        #[test]\n        fn test_i64_logical_roundtrip(\n            values in prop::collection::vec(any::<i64>(), 0..100),\n            logical in any::<LogicalEncoder>(),\n        ) {\n            let mut enc = Encoder::with_explicit(\n                Encoder::default().cfg,\n                ExplicitEncoder::all(IntEncoder::varint_with(logical)),\n            );\n            let mut codecs = Codecs::default();\n            let ctx = StreamCtx::prop_data(\"test\");\n            codecs.write_int_stream(&values, &ctx, &mut enc).unwrap();\n            let parsed = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n            let decoded = parsed.decode_i64s(&mut dec()).unwrap();\n            prop_assert_eq!(decoded, values);\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/mod.rs",
    "content": "mod codecs;\nmod encode_stream;\npub(crate) use encode_stream::dedup_strings;\nmod encoder;\npub(crate) mod logical;\nmod model;\nmod optimizer;\nmod physical;\n#[cfg(test)]\nmod tests;\npub(crate) mod write;\n\n#[cfg(feature = \"__private\")]\npub use codecs::{Codecs, PhysicalCodecs};\n#[cfg(not(feature = \"__private\"))]\npub(crate) use codecs::{Codecs, PhysicalCodecs};\npub use encoder::IntEncoder;\n#[cfg(feature = \"__private\")]\npub use logical::LogicalEncoder;\n#[cfg(all(test, not(feature = \"__private\")))]\npub(crate) use logical::LogicalEncoder;\n#[cfg(any(test, feature = \"__private\"))]\npub use model::*;\n#[cfg(feature = \"__private\")]\npub use physical::PhysicalEncoder;\n#[cfg(all(test, not(feature = \"__private\")))]\npub(crate) use physical::PhysicalEncoder;\npub(crate) use write::write_stream_payload;\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/model.rs",
    "content": "#[cfg(any(test, feature = \"__private\"))]\nuse std::io::Write;\n\n#[cfg(any(test, feature = \"__private\"))]\nuse crate::decoder::StreamMeta;\n\n/// Owned variant of [`RawStream`](crate::decoder::RawStream).\n#[cfg(any(test, feature = \"__private\"))]\n#[derive(Debug, Clone, PartialEq)]\npub struct EncodedStream {\n    pub meta: StreamMeta,\n    pub(crate) data: Vec<u8>,\n}\n\n#[cfg(any(test, feature = \"__private\"))]\nimpl EncodedStream {\n    pub fn write_to<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {\n        writer.write_all(&self.data)\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/optimizer.rs",
    "content": "use num_traits::{AsPrimitive as _, PrimInt as _, WrappingSub, Zero as _};\nuse zigzag::ZigZag;\n\n/// Minimum number of values to profile / compete on.\n///\n/// Below this threshold the full slice is used regardless of its length.\nconst MIN_SAMPLE: usize = 512;\n\n/// Hard upper bound on competition sample size.\nconst MAX_SAMPLE: usize = 16_384;\n\n/// RLE is only worthwhile when runs are on average at least this long.\nconst RLE_MIN_AVG_RUN_LENGTH: f64 = 2.0;\n\n/// Sampling-based encoder selection\n#[derive(Debug, Clone, Default)]\npub struct DataProfile {\n    /// Average run length in the sample.\n    ///\n    /// A run is a maximal sequence of identical consecutive values.\n    /// `avg_run_length = sample_len / num_runs`.\n    avg_run_length: f64,\n\n    /// Average run length of the zigzag-encoded delta stream.\n    ///\n    /// This is computed over the deltas between consecutive sample values,\n    /// excluding the initial/base value, so the effective stream length is\n    /// `sample_len - 1`.\n    ///\n    /// For sequential data like `[1, 2, 3, ...]` the raw values are all\n    /// distinct (`avg_run_length == 1`) but the delta stream is constant, so\n    /// `delta_avg_run_length == N - 1`, making `DeltaRle` extremely effective.\n    delta_avg_run_length: f64,\n\n    /// `true` if the sample values are sorted in ascending or descending order.\n    is_sorted: bool,\n\n    /// Sum of per-value bit widths across the sample.\n    ///\n    /// Aggregate bit count captures the *typical* benefit of delta encoding,\n    /// unlike max bit width which can be pinned by a single outlier.\n    total_bits: u32,\n\n    /// Sum of per-value bit widths of the zigzag-delta stream.\n    delta_total_bits: u32,\n}\n\nimpl DataProfile {\n    /// Profile a `u32` sample in a single pass.\n    #[must_use]\n    #[expect(clippy::cast_precision_loss)]\n    pub(crate) fn profile<T>(sample: &[T::UInt]) -> Self\n    where\n        T: ZigZag,\n        <T as ZigZag>::UInt: WrappingSub,\n    {\n        if sample.is_empty() {\n            return Self::default();\n        }\n\n        let type_bits = T::zero().leading_zeros();\n        let mut runs: usize = 1;\n        let mut delta_runs: usize = 1;\n        let mut is_sorted_rising = true;\n        let mut is_sorted_falling = true;\n        let mut total_bits: u32 = type_bits - sample[0].leading_zeros();\n        let mut delta_total_bits: u32 = 0;\n        let mut prev = sample[0];\n        let mut prev_zz: T::UInt = T::UInt::zero();\n\n        for (i, &v) in sample[1..].iter().enumerate() {\n            if v != prev {\n                runs += 1;\n            }\n            if v < prev {\n                is_sorted_rising = false;\n            } else if prev < v {\n                is_sorted_falling = false;\n            }\n            let delta_bits: T::UInt = v.wrapping_sub(&prev);\n            let delta_signed: T = delta_bits.as_();\n            let zz = T::encode(delta_signed);\n            if i == 0 {\n                prev_zz = zz;\n            } else if zz != prev_zz {\n                delta_runs += 1;\n                prev_zz = zz;\n            }\n            total_bits += type_bits - v.leading_zeros();\n            delta_total_bits += type_bits - zz.leading_zeros();\n            prev = v;\n        }\n\n        let delta_len = sample.len().saturating_sub(1).max(1);\n        Self {\n            avg_run_length: sample.len() as f64 / runs as f64,\n            delta_avg_run_length: delta_len as f64 / delta_runs as f64,\n            is_sorted: is_sorted_rising || is_sorted_falling,\n            total_bits,\n            delta_total_bits,\n        }\n    }\n\n    pub(crate) fn take_sample<T>(values: &[T]) -> &[T] {\n        let len = (values.len() / 100).clamp(MIN_SAMPLE, MAX_SAMPLE);\n        if values.len() <= len {\n            values\n        } else {\n            let start = (values.len() / 2).saturating_sub(len / 2);\n            &values[start..start + len]\n        }\n    }\n\n    /// Returns `true` if RLE is a sensible candidate based on this profile.\n    ///\n    /// An average run length above the threshold means values repeat frequently\n    /// enough that the run-length and unique-value arrays will be compact.\n    #[must_use]\n    pub(crate) fn rle_is_viable(&self) -> bool {\n        self.avg_run_length >= RLE_MIN_AVG_RUN_LENGTH\n    }\n\n    /// Returns `true` if RLE on the delta-transformed stream is viable.\n    ///\n    /// For sequential or constant-delta data the raw values are all distinct\n    /// but the zigzag-delta values are identical, making `DeltaRle` optimal.\n    #[must_use]\n    pub(crate) fn delta_rle_is_viable(&self) -> bool {\n        self.delta_avg_run_length >= RLE_MIN_AVG_RUN_LENGTH\n    }\n\n    /// Returns `true` if Delta encoding is expected to be beneficial.\n    #[must_use]\n    pub(crate) fn delta_is_beneficial(&self) -> bool {\n        self.is_sorted || self.delta_total_bits < self.total_bits\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn assert_profile_flags(profile: &DataProfile, delta: bool, rle: bool, delta_rle: bool) {\n        assert_eq!(profile.delta_is_beneficial(), delta, \"delta\");\n        assert_eq!(profile.rle_is_viable(), rle, \"rle\");\n        assert_eq!(profile.delta_rle_is_viable(), delta_rle, \"delta_rle\");\n    }\n\n    #[test]\n    fn profile_sequential_u32_uses_delta_rle_without_raw_rle() {\n        // All-distinct stream -> avg_run_length == 1 -> no raw-RLE candidate.\n        // But sequential data has constant deltas -> delta-RLE is viable.\n        let data: Vec<u32> = (0..100).collect();\n        let profile = DataProfile::profile::<i32>(DataProfile::take_sample(&data));\n        assert!(profile.is_sorted);\n        assert_profile_flags(&profile, true, false, true);\n    }\n\n    #[test]\n    fn profile_constant_u32_uses_raw_and_delta_rle() {\n        let data = vec![1234u32; 500];\n        let profile = DataProfile::profile::<i32>(DataProfile::take_sample(&data));\n        assert!(profile.is_sorted);\n        assert_profile_flags(&profile, true, true, true);\n    }\n\n    #[test]\n    fn profile_sequential_u64_uses_delta_rle_without_raw_rle() {\n        let data: Vec<u64> = (0u64..500).collect();\n        let profile = DataProfile::profile::<i64>(DataProfile::take_sample(&data));\n        assert!(profile.is_sorted);\n        assert_profile_flags(&profile, true, false, true);\n    }\n\n    #[test]\n    fn profile_empty_has_no_optimizing_flags() {\n        let values: &[u32] = &[];\n        let profile = DataProfile::profile::<i32>(DataProfile::take_sample(values));\n        assert!(!profile.is_sorted);\n        assert_profile_flags(&profile, false, false, false);\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/physical.rs",
    "content": "use crate::decoder::PhysicalEncoding;\nuse crate::{MltError, MltResult};\n\nimpl PhysicalEncoding {\n    pub fn parse(value: u8) -> MltResult<Self> {\n        Self::try_from(value).or(Err(MltError::ParsingPhysicalEncoding(value)))\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]\n#[cfg_attr(test, derive(proptest_derive::Arbitrary))]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub enum PhysicalEncoder {\n    None,\n    /// Can produce better results in combination with a heavyweight compression scheme like `Gzip`.\n    /// Simple compression scheme where the encoding is easier to implement compared to `FastPFOR`.\n    VarInt,\n    /// Preferred, tends to produce the best compression ratio and decoding performance.\n    ///\n    /// Does not support u64/i64 integers\n    FastPFOR,\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/tests.rs",
    "content": "use proptest::prelude::*;\nuse rstest::rstest;\n\nuse crate::MltError;\nuse crate::decoder::{\n    DictionaryType, IntEncoding, LengthType, LogicalEncoding, LogicalValue, Morton, OffsetType,\n    PhysicalEncoding, RawStream, RleMeta, StreamMeta, StreamType,\n};\nuse crate::encoder::model::StreamCtx;\nuse crate::encoder::{\n    Codecs, EncodedStream, Encoder, ExplicitEncoder, IntEncoder, PhysicalEncoder,\n};\nuse crate::test_helpers::{assert_empty, dec, parser};\nuse crate::utils::BinarySerializer as _;\n\nfn roundtrip_stream<'a>(buffer: &'a mut Vec<u8>, stream: &EncodedStream) -> RawStream<'a> {\n    buffer.clear();\n    buffer.write_stream(stream).unwrap();\n    assert_empty(RawStream::from_bytes(buffer, &mut parser()))\n}\n\nfn roundtrip_stream_u32s(wire: &[u8]) -> Vec<u32> {\n    let parsed_stream = assert_empty(RawStream::from_bytes(wire, &mut parser()));\n\n    let mut decoder = dec();\n    let values = parsed_stream.decode_u32s(&mut decoder).unwrap();\n    if !values.is_empty() {\n        assert!(\n            decoder.consumed() > 0,\n            \"decoder should consume bytes after decode\"\n        );\n    }\n    values\n}\n\nfn make_logical_val(logical_encoding: LogicalEncoding, num_values: usize) -> LogicalValue {\n    LogicalValue::new(\n        StreamMeta::new2(\n            StreamType::Data(DictionaryType::None),\n            logical_encoding,\n            PhysicalEncoding::VarInt,\n            num_values,\n        )\n        .unwrap(),\n    )\n}\n\n/// Test case for stream decoding tests\n#[derive(Debug)]\nstruct StreamTestCase {\n    meta: StreamMeta,\n    data: &'static [u8],\n    /// Expected contents of the physical decode buffer after `decode_bits_u32`.\n    expected_u32_logical_value: Option<Vec<u32>>,\n}\n\n/// Generator function that creates a set of test cases for stream decoding\nfn generate_stream_test_cases() -> Vec<StreamTestCase> {\n    vec![\n        // Basic VarInt test case\n        StreamTestCase {\n            meta: StreamMeta::new(\n                StreamType::Data(DictionaryType::None),\n                IntEncoding::new(LogicalEncoding::None, PhysicalEncoding::VarInt),\n                4,\n            ),\n            data: &[0x04, 0x03, 0x02, 0x01],\n            expected_u32_logical_value: Some(vec![4, 3, 2, 1]),\n        },\n        // Basic Encoded test case\n        StreamTestCase {\n            meta: StreamMeta::new(\n                StreamType::Data(DictionaryType::None),\n                IntEncoding::none(),\n                1,\n            ),\n            data: &[0x04, 0x03, 0x02, 0x01],\n            expected_u32_logical_value: Some(vec![0x0102_0304]),\n        },\n    ]\n}\n\nfn create_stream_from_test_case(test_case: &StreamTestCase) -> RawStream<'_> {\n    RawStream::new(test_case.meta, test_case.data)\n}\n\n#[test]\nfn test_decode_bits_u32() {\n    let test_cases = generate_stream_test_cases();\n\n    for test_case in test_cases {\n        if let Some(expected_buf) = &test_case.expected_u32_logical_value {\n            let stream = create_stream_from_test_case(&test_case);\n            let mut buf = Vec::new();\n            stream\n                .decode_bits_u32(&mut buf, &mut dec())\n                .expect(\"Should successfully decode u32 values\");\n            assert_eq!(\n                &buf, expected_buf,\n                \"Should produce decoded u32 values correctly\"\n            );\n        }\n    }\n}\n\n#[rstest]\n// ZigZag pairs: [(0,0),(2,4),(2,4)] -> [(0,0),(1,2),(1,2)]\n// Delta: [(0,0),(1,2),(1,2)] -> [(0,0),(1,2),(2,4)]\n#[case::componentwise_delta(LogicalEncoding::ComponentwiseDelta, vec![0u32, 0, 2, 4, 2, 4], vec![0i32, 0, 1, 2, 2, 4]\n)]\n// ZigZag: [0,1,2,1,2] -> [0,-1,1,-1,1]\n// Delta: [0,-1,1,-1,1] -> [0,-1,0,-1,0]\n#[case::delta(LogicalEncoding::Delta, vec![0u32, 1, 2, 1, 2], vec![0i32, -1, 0, -1, 0])]\n// RLE: [3,2] [0,2] -> [0,0,0,2,2]\n// ZigZag: [0,0,0,2,2] -> [0,0,0,1,1]\n// Delta: [0,0,0,1,1] -> [0,0,0,1,2]\n#[case::delta_rle(LogicalEncoding::DeltaRle(RleMeta { runs: 2, num_rle_values: 5 }), vec![3u32, 2, 0, 2], vec![0i32, 0, 0, 1, 2]\n)]\n#[case::delta_empty(LogicalEncoding::Delta, vec![], vec![])]\nfn test_decode_i32(\n    #[case] logical_encoding: LogicalEncoding,\n    #[case] input_data: Vec<u32>,\n    #[case] expected: Vec<i32>,\n) {\n    let result =\n        make_logical_val(logical_encoding, input_data.len()).decode_i32(&input_data, &mut dec());\n    assert!(result.is_ok(), \"should decode successfully\");\n    assert_eq!(result.unwrap(), expected, \"should match expected output\");\n}\n\n#[rstest]\n#[case::empty(LogicalEncoding::None, vec![], vec![])]\n#[case::new_encoded(LogicalEncoding::None, vec![10u32, 20, 30, 40], vec![10u32, 20, 30, 40])]\n#[case::rle(LogicalEncoding::Rle(RleMeta { runs: 3, num_rle_values: 6 }), vec![3u32, 2, 1, 10, 20, 30], vec![10u32, 10, 10, 20, 20, 30]\n)]\n// ZigZag: [0,2,2,2,2] -> [0,1,1,1,1]\n// Delta: [0,1,1,1,1] -> [0,1,2,3,4]\n#[case::delta(LogicalEncoding::Delta, vec![0u32, 2, 2, 2, 2], vec![0u32, 1, 2, 3, 4])]\nfn test_decode_u32(\n    #[case] logical_encoding: LogicalEncoding,\n    #[case] input_data: Vec<u32>,\n    #[case] expected: Vec<u32>,\n) {\n    let result =\n        make_logical_val(logical_encoding, input_data.len()).decode_u32(&input_data, &mut dec());\n    assert!(result.is_ok(), \"should decode successfully\");\n    assert_eq!(result.unwrap(), expected, \"should match expected output\");\n}\n\n#[rstest]\n#[case::basic(vec![1, 2, 3, 4, 5, 100, 1000])]\n#[case::large(vec![1_000_000; 256])]\n#[case::edge_values(vec![0, 1, 2, 4, 8, 16, 1024, 65535, 1_000_000_000, u32::MAX])]\n#[case::empty(vec![])]\nfn test_fastpfor_roundtrip(#[case] values: Vec<u32>) {\n    let mut enc = Encoder::with_explicit(\n        Encoder::default().cfg,\n        ExplicitEncoder::all(IntEncoder::fastpfor()),\n    );\n    let codecs = &mut Codecs::default();\n    let ctx = StreamCtx::prop_data(\"test\");\n    codecs.write_int_stream(&values, &ctx, &mut enc).unwrap();\n    let decoded_values = roundtrip_stream_u32s(&enc.data);\n    assert_eq!(decoded_values, values);\n}\n\n/// Test roundtrip: write -> parse -> equality for stream serialization\n#[rstest]\n#[case::new_encoded(StreamType::Data(DictionaryType::None), 2, LogicalEncoding::None, PhysicalEncoding::None, vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], false\n)]\n#[case::new_encoded(StreamType::Data(DictionaryType::None), 2, LogicalEncoding::ComponentwiseDelta, PhysicalEncoding::None, vec![0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00], false\n)]\n#[case::new_encoded(StreamType::Offset(OffsetType::Vertex), 3, LogicalEncoding::None, PhysicalEncoding::None, vec![0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00], false\n)]\n#[case::varint(StreamType::Data(DictionaryType::None), 4, LogicalEncoding::None, PhysicalEncoding::VarInt, vec![0x0A, 0x14, 0x1E, 0x28], false\n)]\n#[case::varint(StreamType::Data(DictionaryType::None), 5, LogicalEncoding::Delta, PhysicalEncoding::VarInt, vec![0x00, 0x02, 0x02, 0x02, 0x02], false\n)]\n#[case::varint(StreamType::Data(DictionaryType::None), 3, LogicalEncoding::PseudoDecimal, PhysicalEncoding::VarInt, vec![0x01, 0x02, 0x03], false\n)]\n#[case::varint(StreamType::Length(LengthType::VarBinary), 3, LogicalEncoding::Delta, PhysicalEncoding::VarInt, vec![0x00, 0x02, 0x02], false\n)]\n#[case::rle(StreamType::Data(DictionaryType::None), 6, LogicalEncoding::Rle(RleMeta { runs: 3, num_rle_values: 6 }), PhysicalEncoding::VarInt, vec![0x03, 0x02, 0x01, 0x0A, 0x14, 0x1E], false\n)]\n#[case::rle(StreamType::Data(DictionaryType::None), 5, LogicalEncoding::DeltaRle(RleMeta { runs: 2, num_rle_values: 5 }), PhysicalEncoding::VarInt, vec![0x03, 0x02, 0x00, 0x02], false\n)]\n#[case::morton(StreamType::Data(DictionaryType::Morton), 4, LogicalEncoding::Morton(Morton { bits: 16, shift: 0 }), PhysicalEncoding::VarInt, vec![0x01, 0x02, 0x03, 0x04], false\n)]\n#[case::boolean(StreamType::Present, 16, LogicalEncoding::Rle(RleMeta { runs: 2, num_rle_values: 2 }), PhysicalEncoding::VarInt, vec![0xFF, 0x00], true\n)]\nfn test_stream_roundtrip(\n    #[case] stream_type: StreamType,\n    #[case] num_values: u32,\n    #[case] logical_encoding: LogicalEncoding,\n    #[case] physical_encoding: PhysicalEncoding,\n    #[case] data_bytes: Vec<u8>,\n    #[case] is_bool: bool,\n) {\n    let stream = EncodedStream {\n        meta: StreamMeta::new(\n            stream_type,\n            IntEncoding::new(logical_encoding, physical_encoding),\n            num_values,\n        ),\n        data: data_bytes,\n    };\n\n    // Write to buffer\n    let mut buffer = Vec::new();\n    if is_bool {\n        buffer.write_boolean_stream(&stream).unwrap();\n    } else {\n        buffer.write_stream(&stream).unwrap();\n    }\n\n    // Parse back\n    let parsed = assert_empty(if is_bool {\n        RawStream::parse_bool(&buffer, &mut parser())\n    } else {\n        RawStream::from_bytes(&buffer, &mut parser())\n    });\n\n    assert_eq!(parsed.meta, stream.meta, \"metadata mismatch\");\n    assert_eq!(stream.data.as_slice(), parsed.data, \"data mismatch\");\n}\n\n#[test]\nfn test_morton_parse_rejects_too_many_bits() {\n    let stream = EncodedStream {\n        meta: StreamMeta::new(\n            StreamType::Data(DictionaryType::Morton),\n            IntEncoding::new(\n                LogicalEncoding::Morton(Morton { bits: 17, shift: 0 }),\n                PhysicalEncoding::VarInt,\n            ),\n            1,\n        ),\n        data: vec![0],\n    };\n    let mut buffer = Vec::new();\n    buffer.write_stream(&stream).unwrap();\n\n    let err = RawStream::from_bytes(&buffer, &mut parser()).unwrap_err();\n    assert!(matches!(err, MltError::InvalidMortonBits(17)));\n}\n\n/// OOM regression: `VarInt` stream with huge `num_values` but `byte_length=0`.\n///\n/// `Wire: stream_type=0x00 | enc=0x02(VarInt) | num_values=0xd5_ff_d5_ff_03 | byte_length=0x00`\n/// Before the budget fix, `parse_varint_vec` called `Vec::with_capacity(1_073_053_653)` → ~4 GB OOM.\n/// Now the memory budget is checked at parse time: `num_values * 8 = ~8 GB > 10 MB limit`.\n#[test]\nfn test_varint_stream_huge_num_values_empty_data() {\n    // enc_byte = 0x02 → logical1=0(None), logical2=0(None), physical=2(VarInt)\n    // num_values = 0xd5 0xff 0xd5 0xff 0x03 = 1_073_053_653 (valid u32, 5-byte varint)\n    // byte_length = 0x00 → 0 bytes of data\n    let wire: &[u8] = &[0x00, 0x02, 0xd5, 0xff, 0xd5, 0xff, 0x03, 0x00];\n    // Parsing must fail: budget reserves num_values * 8 ≈ 8 GB which exceeds the 10 MB limit.\n    let result = RawStream::from_bytes(wire, &mut parser());\n    assert!(\n        result.is_err(),\n        \"parse must fail when num_values * 8 exceeds the memory budget\"\n    );\n}\n\n/// RLE mismatch regression: `num_rle_values` in stream header doesn't equal sum of runs.\n///\n/// `RleMeta::decode` must return an error instead of allocating based on the\n/// header-declared `num_rle_values` when the actual run sum differs.\n#[test]\nfn test_rle_num_rle_values_mismatch() {\n    // runs=1, num_rle_values=u32::MAX (declared), but the single run has value 1.\n    // Sum of runs = 1 ≠ u32::MAX → must error before allocating ~16 GB.\n    let rle = RleMeta {\n        runs: 1,\n        num_rle_values: u32::MAX,\n    };\n    // data = [run_len=1, value=42] (1 run of length 1 with value 42)\n    let data = [1u32, 42u32];\n    let result = rle.decode::<u32>(&data, &mut dec());\n    assert!(\n        result.is_err(),\n        \"must reject mismatched num_rle_values before allocating\"\n    );\n}\n\nfn encoding_no_fastpfor() -> impl Strategy<Value = IntEncoder> {\n    any::<IntEncoder>().prop_filter(\"not fastpfor\", |v| v.physical != PhysicalEncoder::FastPFOR)\n}\n\n/// Deduplicate strings and return (`offset_indices`, `unique_lengths`).\nfn dedup_and_get_parts(values: &[&str]) -> (Vec<u32>, Vec<u32>) {\n    use crate::encoder::stream::dedup_strings;\n    use crate::utils::strings_to_lengths;\n    let (unique, offset_indices) = dedup_strings(values).unwrap();\n    let lengths = strings_to_lengths(&unique).unwrap();\n    (offset_indices, lengths)\n}\n\n#[rstest]\n#[case::with_duplicates(&[\"apple\", \"banana\", \"apple\", \"cherry\", \"banana\", \"apple\"], &[0, 1, 0, 2, 1, 0], &[5, 6, 6]\n)]\n#[case::all_unique(&[\"a\", \"b\", \"c\", \"d\"], &[0, 1, 2, 3], &[1, 1, 1, 1])]\n#[case::all_same(&[\"same\", \"same\", \"same\", \"same\"], &[0, 0, 0, 0], &[4])]\nfn test_encode_strings_dict(\n    #[case] values: &[&str],\n    #[case] expected_offsets: &[u32],\n    #[case] expected_lengths: &[u32],\n) {\n    let (offsets, lengths) = dedup_and_get_parts(values);\n    assert_eq!(offsets, expected_offsets);\n    assert_eq!(lengths, expected_lengths);\n}\n\nproptest! {\n    #[test]\n    fn test_i8_roundtrip(\n        values in prop::collection::vec(any::<i8>(), 0..100),\n        encoding in any::<IntEncoder>(),\n    ) {\n        let widened: Vec<i32> = values.iter().map(|&v| i32::from(v)).collect();\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&widened, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let parsed_stream = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n        let decoded_values = parsed_stream.decode_i8s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_u8_roundtrip(\n        values in prop::collection::vec(any::<u8>(), 0..100),\n        encoding in any::<IntEncoder>()\n    ) {\n        let widened: Vec<u32> = values.iter().map(|&v| u32::from(v)).collect();\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&widened, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let parsed_stream = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n        let decoded_values = parsed_stream.decode_u8s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_u32_roundtrip(\n        values in prop::collection::vec(any::<u32>(), 0..100),\n        encoding in any::<IntEncoder>()\n    ) {\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&values, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let decoded_values = roundtrip_stream_u32s(&enc.data);\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_i32_roundtrip(\n        values in prop::collection::vec(any::<i32>(), 0..100),\n        encoding in any::<IntEncoder>(),\n    ) {\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&values, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let parsed_stream = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n        let decoded_values = parsed_stream.decode_i32s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_u64_roundtrip(\n        values in prop::collection::vec(any::<u64>(), 0..100),\n        encoding in encoding_no_fastpfor()\n    ) {\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&values, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let parsed_stream = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n        let decoded_values = parsed_stream.decode_u64s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_i64_roundtrip(\n        values in prop::collection::vec(any::<i64>(), 0..100),\n        encoding in encoding_no_fastpfor()\n    ) {\n        let mut enc = Encoder::with_explicit(Encoder::default().cfg, ExplicitEncoder::all(encoding));\n        let mut codecs = Codecs::default();\n        codecs.write_int_stream(&values, &StreamCtx::prop_data(\"test\"), &mut enc).unwrap();\n        let parsed_stream = assert_empty(RawStream::from_bytes(&enc.data, &mut parser()));\n        let decoded_values = parsed_stream.decode_i64s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values, values);\n    }\n\n    #[test]\n    fn test_f32_roundtrip(values in prop::collection::vec(any::<f32>(), 0..100)) {\n        let owned_stream = EncodedStream::encode_f32(&values).unwrap();\n\n        let mut buf = Vec::new();\n        let parsed_stream = roundtrip_stream(&mut buf, &owned_stream);\n        let decoded_values = parsed_stream.decode_f32s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values.len(), values.len());\n        for (v1, v2) in decoded_values.iter().zip(values.iter()) {\n            assert_eq!(\n                v1.to_bits(),\n                v2.to_bits(),\n                \"despite being semantically equal, the values are not actually equal\"\n            );\n        }\n    }\n\n    #[test]\n    fn test_f64_roundtrip(values in prop::collection::vec(any::<f64>(), 0..100)) {\n        let owned_stream = EncodedStream::encode_f64(&values).unwrap();\n\n        let mut buf = Vec::new();\n        let parsed_stream = roundtrip_stream(&mut buf, &owned_stream);\n        let decoded_values = parsed_stream.decode_f64s(&mut dec()).unwrap();\n\n        assert_eq!(decoded_values.len(), values.len());\n        for (v1, v2) in decoded_values.iter().zip(values.iter()) {\n            assert_eq!(\n                v1.to_bits(),\n                v2.to_bits(),\n                \"despite being semantically equal, the values are not actually equal\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/stream/write.rs",
    "content": "use bytemuck::{NoUninit, cast_slice};\nuse fastpfor::AnyLenCodec as _;\nuse integer_encoding::VarInt;\nuse num_traits::PrimInt;\nuse zigzag::ZigZag;\n\nuse crate::MltError::UnsupportedPhysicalEncoding;\nuse crate::MltResult;\nuse crate::codecs::zigzag::{encode_zigzag, encode_zigzag_delta};\nuse crate::decoder::{LogicalEncoding, PhysicalEncoding, StreamMeta, StreamType};\nuse crate::encoder::Encoder;\nuse crate::encoder::model::StreamCtx;\nuse crate::encoder::stream::codecs::{LogicalCodecs, PhysicalCodecs};\nuse crate::encoder::stream::logical::apply_rle;\nuse crate::encoder::stream::physical::PhysicalEncoder;\nuse crate::encoder::writer::AltSession;\n\n#[inline]\npub(crate) fn write_stream_payload(\n    data: &mut Vec<u8>,\n    meta: StreamMeta,\n    is_boolean: bool,\n    payload: &[u8],\n) -> MltResult<()> {\n    let byte_length = u32::try_from(payload.len())?;\n    meta.write_to(data, is_boolean, byte_length)?;\n    data.extend_from_slice(payload);\n    Ok(())\n}\n\npub(crate) trait PhysicalIntStreamKind {\n    type Value: Into<u64> + NoUninit + PrimInt;\n    const FASTPFOR_ALLOWED: bool;\n\n    #[cfg(target_endian = \"little\")]\n    fn none<'a>(_physical: &'a mut PhysicalCodecs, values: &'a [Self::Value]) -> &'a [u8] {\n        cast_slice(values)\n    }\n    #[cfg(not(target_endian = \"little\"))]\n    fn none<'a>(physical: &'a mut PhysicalCodecs, values: &'a [Self::Value]) -> &'a [u8] {\n        compile_error!(\"PhysicalEncoding::none is not implemented for non-little-endian targets\");\n    }\n\n    fn fastpfor<'a>(\n        physical: &'a mut PhysicalCodecs,\n        values: &'a [Self::Value],\n    ) -> MltResult<&'a [u8]>;\n}\n\nimpl PhysicalCodecs {\n    pub(crate) fn varint<T>(&mut self, values: &[T]) -> &[u8]\n    where\n        T: Copy + Into<u64>,\n    {\n        // encode_var writes to a stack buffer; avoids the Vec<u8> allocation\n        // that encode_var_vec() would produce for every value.\n        self.u8_tmp.clear();\n        let mut buf = [0u8; 10];\n        for &v in values {\n            let n = v.into().encode_var(&mut buf);\n            self.u8_tmp.extend_from_slice(&buf[..n]);\n        }\n        &self.u8_tmp\n    }\n\n    pub(crate) fn fastpfor(&mut self, values: &[u32]) -> MltResult<&[u8]> {\n        self.u8_tmp.clear();\n        if !values.is_empty() {\n            self.u32_tmp.clear();\n            self.fastpfor.encode(values, &mut self.u32_tmp)?;\n            for word in &mut self.u32_tmp {\n                *word = word.to_be();\n            }\n            self.u8_tmp.extend_from_slice(cast_slice(&self.u32_tmp));\n        }\n        Ok(&self.u8_tmp)\n    }\n\n    /// Physically encode and write stream to the output.\n    pub(crate) fn write_encoded_as<P: PhysicalIntStreamKind + ?Sized>(\n        &mut self,\n        ctx: &StreamCtx,\n        enc: &mut Encoder,\n        le: LogicalEncoding,\n        values: &[P::Value],\n        encode_as: PhysicalEncoder,\n    ) -> MltResult<()> {\n        use PhysicalEncoding as PE;\n        let (pe, vals) = match encode_as {\n            PhysicalEncoder::None => (PE::None, P::none(self, values)),\n            PhysicalEncoder::VarInt => (PE::VarInt, self.varint(values)),\n            PhysicalEncoder::FastPFOR => (PE::FastPFor256, P::fastpfor(self, values)?),\n        };\n        let meta = StreamMeta::new2(ctx.stream_type, le, pe, values.len())?;\n        write_stream_payload(&mut enc.data, meta, false, vals)\n    }\n\n    pub(crate) fn write_alternatives<P: PhysicalIntStreamKind + ?Sized>(\n        &mut self,\n        alt: &mut AltSession<'_>,\n        values: &[P::Value],\n        logical: LogicalEncoding,\n        stream_type: StreamType,\n    ) -> MltResult<()> {\n        use PhysicalEncoding as PE;\n        if P::FASTPFOR_ALLOWED {\n            alt.with(|enc| {\n                let meta = StreamMeta::new2(stream_type, logical, PE::FastPFor256, values.len())?;\n                write_stream_payload(&mut enc.data, meta, false, P::fastpfor(self, values)?)\n            })?;\n        }\n        alt.with(|enc| {\n            let meta = StreamMeta::new2(stream_type, logical, PE::VarInt, values.len())?;\n            write_stream_payload(&mut enc.data, meta, false, self.varint(values))\n        })\n    }\n}\n\nimpl PhysicalIntStreamKind for [u32] {\n    type Value = u32;\n    const FASTPFOR_ALLOWED: bool = true;\n\n    fn fastpfor<'a>(\n        physical: &'a mut PhysicalCodecs,\n        values: &'a [Self::Value],\n    ) -> MltResult<&'a [u8]> {\n        physical.fastpfor(values)\n    }\n}\n\nimpl PhysicalIntStreamKind for [u64] {\n    type Value = u64;\n    const FASTPFOR_ALLOWED: bool = false;\n\n    fn fastpfor<'a>(\n        _physical: &'a mut PhysicalCodecs,\n        _values: &'a [Self::Value],\n    ) -> MltResult<&'a [u8]> {\n        Err(UnsupportedPhysicalEncoding(\"FastPFOR on u64\"))?\n    }\n}\n\npub(crate) trait LogicalIntStreamKind {\n    type Input;\n    type Output: PhysicalIntStreamKind + ?Sized;\n    type Profile: ZigZag<UInt = <Self::Output as PhysicalIntStreamKind>::Value>;\n}\n\npub(crate) trait LogicalIntCodec<T: LogicalIntStreamKind + ?Sized> {\n    fn none<'a>(&'a mut self, values: &'a T) -> &'a [<T::Output as PhysicalIntStreamKind>::Value];\n\n    fn delta<'a>(&'a mut self, values: &'a T) -> &'a [<T::Output as PhysicalIntStreamKind>::Value];\n\n    fn rle<'a>(\n        &'a mut self,\n        values: &'a T,\n    ) -> MltResult<(\n        LogicalEncoding,\n        &'a [<T::Output as PhysicalIntStreamKind>::Value],\n    )>;\n\n    fn delta_rle<'a>(\n        &'a mut self,\n        values: &'a T,\n    ) -> MltResult<(\n        LogicalEncoding,\n        &'a [<T::Output as PhysicalIntStreamKind>::Value],\n    )>;\n}\n\nfn encode_u8_as_u32<'a>(values: &[u8], target: &'a mut Vec<u32>) -> &'a [u32] {\n    target.clear();\n    target.extend(values.iter().map(|&v| u32::from(v)));\n    target\n}\n\nfn encode_i8_zigzag<'a>(values: &[i8], target: &'a mut Vec<u32>) -> &'a [u32] {\n    target.clear();\n    target.extend(values.iter().map(|&v| i32::encode(i32::from(v))));\n    target\n}\n\nfn encode_u8_delta<'a>(values: &[u8], target: &'a mut Vec<u32>) -> &'a [u32] {\n    target.clear();\n    target.reserve(values.len());\n    let mut prev = 0_i32;\n    for &v in values {\n        let v = i32::from(v);\n        target.push(i32::encode(v.wrapping_sub(prev)));\n        prev = v;\n    }\n    target\n}\n\nfn encode_i8_delta<'a>(values: &[i8], target: &'a mut Vec<u32>) -> &'a [u32] {\n    target.clear();\n    target.reserve(values.len());\n    let mut prev = 0_i32;\n    for &v in values {\n        let v = i32::from(v);\n        target.push(i32::encode(v.wrapping_sub(prev)));\n        prev = v;\n    }\n    target\n}\n\nimpl LogicalIntStreamKind for [u8] {\n    type Input = u8;\n    type Output = [u32];\n    type Profile = i32;\n}\n\nimpl LogicalIntCodec<[u8]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [u8]) -> &'a [u32] {\n        encode_u8_as_u32(values, &mut self.u32_tmp)\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [u8]) -> &'a [u32] {\n        encode_u8_delta(values, &mut self.u32_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [u8]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_u8_as_u32(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u32_tmp2))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [u8]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_u8_delta(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u32_tmp2))\n    }\n}\n\nimpl LogicalIntStreamKind for [i8] {\n    type Input = i8;\n    type Output = [u32];\n    type Profile = i32;\n}\n\nimpl LogicalIntCodec<[i8]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [i8]) -> &'a [u32] {\n        encode_i8_zigzag(values, &mut self.u32_tmp)\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [i8]) -> &'a [u32] {\n        encode_i8_delta(values, &mut self.u32_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [i8]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_i8_zigzag(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u32_tmp2))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [i8]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_i8_delta(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u32_tmp2))\n    }\n}\n\nimpl LogicalIntStreamKind for [u32] {\n    type Input = u32;\n    type Output = [u32];\n    type Profile = i32;\n}\n\nimpl LogicalIntCodec<[u32]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [u32]) -> &'a [u32] {\n        values\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [u32]) -> &'a [u32] {\n        encode_zigzag_delta(cast_slice::<u32, i32>(values), &mut self.u32_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [u32]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let meta = apply_rle(values, values.len(), &mut self.u32_tmp)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u32_tmp))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [u32]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_zigzag_delta(cast_slice::<u32, i32>(values), &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u32_tmp2))\n    }\n}\n\nimpl LogicalIntStreamKind for [i32] {\n    type Input = i32;\n    type Output = [u32];\n    type Profile = i32;\n}\n\nimpl LogicalIntCodec<[i32]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [i32]) -> &'a [u32] {\n        encode_zigzag(values, &mut self.u32_tmp)\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [i32]) -> &'a [u32] {\n        encode_zigzag_delta(values, &mut self.u32_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [i32]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_zigzag(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u32_tmp2))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [i32]) -> MltResult<(LogicalEncoding, &'a [u32])> {\n        let data = encode_zigzag_delta(values, &mut self.u32_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u32_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u32_tmp2))\n    }\n}\n\nimpl LogicalIntStreamKind for [u64] {\n    type Input = u64;\n    type Output = [u64];\n    type Profile = i64;\n}\n\nimpl LogicalIntCodec<[u64]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [u64]) -> &'a [u64] {\n        values\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [u64]) -> &'a [u64] {\n        encode_zigzag_delta(cast_slice::<u64, i64>(values), &mut self.u64_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [u64]) -> MltResult<(LogicalEncoding, &'a [u64])> {\n        let meta = apply_rle(values, values.len(), &mut self.u64_tmp)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u64_tmp))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [u64]) -> MltResult<(LogicalEncoding, &'a [u64])> {\n        let data = encode_zigzag_delta(cast_slice::<u64, i64>(values), &mut self.u64_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u64_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u64_tmp2))\n    }\n}\n\nimpl LogicalIntStreamKind for [i64] {\n    type Input = i64;\n    type Output = [u64];\n    type Profile = i64;\n}\n\nimpl LogicalIntCodec<[i64]> for LogicalCodecs {\n    fn none<'a>(&'a mut self, values: &'a [i64]) -> &'a [u64] {\n        encode_zigzag(values, &mut self.u64_tmp)\n    }\n\n    fn delta<'a>(&'a mut self, values: &'a [i64]) -> &'a [u64] {\n        encode_zigzag_delta(values, &mut self.u64_tmp)\n    }\n\n    fn rle<'a>(&'a mut self, values: &'a [i64]) -> MltResult<(LogicalEncoding, &'a [u64])> {\n        let data = encode_zigzag(values, &mut self.u64_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u64_tmp2)?;\n        Ok((LogicalEncoding::Rle(meta), &self.u64_tmp2))\n    }\n\n    fn delta_rle<'a>(&'a mut self, values: &'a [i64]) -> MltResult<(LogicalEncoding, &'a [u64])> {\n        let data = encode_zigzag_delta(values, &mut self.u64_tmp);\n        let meta = apply_rle(data, values.len(), &mut self.u64_tmp2)?;\n        Ok((LogicalEncoding::DeltaRle(meta), &self.u64_tmp2))\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/tests.rs",
    "content": "use crate::TileLayer;\nuse crate::encoder::model::ColumnKind;\nuse crate::encoder::{ExplicitEncoder, IntEncoder, SortStrategy, StagedLayer, VertexBufferType};\n\nimpl ExplicitEncoder {\n    /// Use `enc` for all integer streams, plain string encoding, and `Vec2` vertex layout.\n    #[must_use]\n    pub fn all(enc: IntEncoder) -> Self {\n        Self {\n            vertex_buffer_type: VertexBufferType::Vec2,\n            force_stream: Box::new(|_| false),\n            get_int_encoder: Box::new(move |_| enc),\n            get_str_encoding: Box::new(|_| crate::encoder::StrEncoding::Plain),\n        }\n    }\n\n    /// Like [`Self::all`] but use `str_enc` for string property columns.\n    #[must_use]\n    pub fn all_with_str(enc: IntEncoder, str_enc: crate::encoder::StrEncoding) -> Self {\n        Self {\n            get_str_encoding: Box::new(move |_| str_enc),\n            ..Self::all(enc)\n        }\n    }\n\n    /// Use `id_enc` for the ID stream; `varint` for all other streams.\n    ///\n    /// Useful for tests that need to pin the exact ID encoding without caring about\n    /// geometry or property streams.\n    #[must_use]\n    pub fn for_id(id_enc: IntEncoder) -> Self {\n        Self {\n            get_int_encoder: Box::new(move |ctx| {\n                if ctx.kind == ColumnKind::Id {\n                    id_enc\n                } else {\n                    IntEncoder::varint()\n                }\n            }),\n            ..Self::all(IntEncoder::varint())\n        }\n    }\n}\n\n#[must_use]\npub fn stage_tile(\n    tile: TileLayer,\n    sort: SortStrategy,\n    allow_shared_dict: bool,\n    tessellate: bool,\n) -> StagedLayer {\n    let analysis = tile.analyze(allow_shared_dict).expect(\"analyze tile\");\n    let curve_params = tile.curve_params();\n    StagedLayer::from_tile(tile, sort, &analysis, tessellate, curve_params)\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/tile.rs",
    "content": "//! Row-oriented \"source form\" for the optimizer.\n//!\n//! [`TileLayer`] holds one [`TileFeature`] per map feature, each owning\n//! its geometry as a [`geo_types::Geometry<i32>`] and its property values as a\n//! plain `Vec<PropValue>`.  This is the working form used throughout the\n//! optimizer and sorting pipeline: it is cheap to clone, trivially sortable,\n//! and free from any encoded/decoded duality.\n//!\n//! Conversion from [`TileLayer`] to [`StagedLayer`] is done via\n//! [`StagedLayer::from_tile`] with pre-computed layer statistics.\n\nuse crate::decoder::{GeometryValues, PropValue, TileFeature, TileLayer};\nuse crate::encoder::model::{CurveParams, StagedLayer};\nuse crate::encoder::optimizer::{LayerStats, Presence, SharedDictRole};\nuse crate::encoder::{SortStrategy, StagedId, StagedProperty, StagedSharedDict};\n\nimpl StagedLayer {\n    /// Construct a [`StagedLayer`] from a row-oriented [`TileLayer`] using\n    /// pre-computed layer statistics and curve parameters.\n    ///\n    /// `curve_params` is taken as a parameter (rather than recomputed here)\n    /// so a single [`TileLayer::curve_params`] scan feeds every sort trial\n    /// and the encoder's dictionary builders.\n    ///\n    /// When `tessellate` is `true`, polygon and multi-polygon geometries have\n    /// their triangulation stored alongside the geometry.\n    #[must_use]\n    #[hotpath::measure]\n    pub fn from_tile(\n        mut source: TileLayer,\n        sort: SortStrategy,\n        stats: &LayerStats,\n        tessellate: bool,\n        curve_params: CurveParams,\n    ) -> Self {\n        assert!(!source.features.is_empty(), \"empty tile\");\n        source.sort(sort, curve_params);\n        let mut geometry = if tessellate {\n            GeometryValues::new_tessellated()\n        } else {\n            GeometryValues::default()\n        };\n        for f in &source.features {\n            geometry.push_geom(&f.geometry);\n        }\n\n        let id = StagedId::from_optional_with_presence(\n            source.features.iter().map(|f| f.id),\n            stats.id.as_ref(),\n        );\n\n        let shared_dict_columns = shared_dict_columns(stats);\n        let mut properties = Vec::with_capacity(source.property_names.len());\n        for (col_idx, shared_cols) in shared_dict_columns\n            .iter()\n            .enumerate()\n            .take(source.property_names.len())\n        {\n            let prop_analysis = stats\n                .properties\n                .get(col_idx)\n                .expect(\"analysis matches source property columns\");\n            match prop_analysis.stats.shared_dict() {\n                SharedDictRole::Owner(prefix) => {\n                    properties.push(build_shared_dict(\n                        col_idx,\n                        &prefix,\n                        shared_cols,\n                        &source.property_names,\n                        stats,\n                        &mut source.features,\n                    ));\n                }\n                SharedDictRole::Member(_) => {}\n                SharedDictRole::None => {\n                    if let Some(prop) = build_scalar_column(\n                        std::mem::take(&mut source.property_names[col_idx]),\n                        col_idx,\n                        prop_analysis.presence,\n                        &mut source.features,\n                    ) {\n                        properties.push(prop);\n                    }\n                }\n            }\n        }\n\n        Self {\n            name: source.name,\n            extent: source.extent,\n            id,\n            geometry,\n            properties,\n        }\n    }\n}\n\nfn shared_dict_columns(stats: &LayerStats) -> Vec<Vec<usize>> {\n    let mut columns = vec![Vec::new(); stats.properties.len()];\n    for (col_idx, prop) in stats.properties.iter().enumerate() {\n        match prop.stats.shared_dict() {\n            SharedDictRole::Owner(_) => columns[col_idx].push(col_idx),\n            SharedDictRole::Member(owner_col) => columns[owner_col].push(col_idx),\n            SharedDictRole::None => {}\n        }\n    }\n    columns\n}\n\nfn build_scalar_column(\n    name: String,\n    col: usize,\n    presence: Presence,\n    features: &mut [TileFeature],\n) -> Option<StagedProperty> {\n    if presence == Presence::AllNull {\n        return None;\n    }\n\n    // Determine the variant by peeking at the first feature value.\n    // Typed nulls (e.g. `PropValue::Bool(None)`) already carry the column type,\n    // so no filtering is needed; only a fully-absent column returns `None` here.\n    // Fall back to `Str` if every feature has no value for this column.\n    let first_val = features.iter().find_map(|f| f.properties.get(col));\n\n    // Presence is precomputed before sort trials; this pass only gathers values\n    // in the selected row order.\n    macro_rules! scalar_col {\n        ($opt_ctor:ident, $non_opt_ctor:ident, $ty:ty, $sv:ident) => {{\n            Some(match presence {\n                Presence::AllNull => unreachable!(\"handled before variant dispatch\"),\n                Presence::AllPresent => StagedProperty::$non_opt_ctor(\n                    name,\n                    features\n                        .iter()\n                        .map(|f| match f.properties.get(col) {\n                            Some(PropValue::$sv(Some(v))) => *v,\n                            _ => unreachable!(\"analysis guarantees present typed values\"),\n                        })\n                        .collect(),\n                ),\n                Presence::Mixed | Presence::SameAsProp(_) => StagedProperty::$opt_ctor(\n                    name,\n                    features.iter().map(|f| match f.properties.get(col) {\n                        Some(PropValue::$sv(v)) => *v,\n                        _ => None,\n                    }),\n                ),\n            })\n        }};\n    }\n\n    match first_val {\n        Some(PropValue::Bool(_)) => scalar_col!(opt_bool, bool, bool, Bool),\n        Some(PropValue::I8(_)) => scalar_col!(opt_i8, i8, i8, I8),\n        Some(PropValue::U8(_)) => scalar_col!(opt_u8, u8, u8, U8),\n        Some(PropValue::I32(_)) => scalar_col!(opt_i32, i32, i32, I32),\n        Some(PropValue::U32(_)) => scalar_col!(opt_u32, u32, u32, U32),\n        Some(PropValue::I64(_)) => scalar_col!(opt_i64, i64, i64, I64),\n        Some(PropValue::U64(_)) => scalar_col!(opt_u64, u64, u64, U64),\n        Some(PropValue::F32(_)) => scalar_col!(opt_f32, f32, f32, F32),\n        Some(PropValue::F64(_)) => scalar_col!(opt_f64, f64, f64, F64),\n        Some(PropValue::Str(_)) | None => Some(match presence {\n            Presence::AllNull => unreachable!(\"handled before variant dispatch\"),\n            Presence::AllPresent => StagedProperty::str(\n                name,\n                features\n                    .iter_mut()\n                    .map(|f| match f.properties.get_mut(col) {\n                        Some(PropValue::Str(Some(v))) => std::mem::take(v),\n                        _ => unreachable!(\"analysis guarantees present string values\"),\n                    }),\n            ),\n            Presence::Mixed | Presence::SameAsProp(_) => StagedProperty::opt_str(\n                name,\n                features\n                    .iter_mut()\n                    .map(|f| match f.properties.get_mut(col) {\n                        Some(PropValue::Str(v)) => v.take(),\n                        _ => None,\n                    }),\n            ),\n        }),\n    }\n}\n\nfn build_shared_dict(\n    owner_col: usize,\n    prefix: &str,\n    shared_dict_columns: &[usize],\n    property_names: &[String],\n    analysis: &LayerStats,\n    features: &mut [TileFeature],\n) -> StagedProperty {\n    debug_assert_eq!(shared_dict_columns.first(), Some(&owner_col));\n    let columns = shared_dict_columns.iter().copied().map(|col_idx| {\n        let name = &property_names[col_idx];\n        let suffix = name.strip_prefix(prefix).unwrap_or(name).to_owned();\n        let values: Vec<Option<String>> = features\n            .iter_mut()\n            .map(|f| match f.properties.get_mut(col_idx) {\n                Some(PropValue::Str(s)) => s.take(),\n                _ => None,\n            })\n            .collect();\n        let presence = analysis.properties[col_idx].presence;\n        (suffix, values, presence)\n    });\n\n    StagedProperty::SharedDict(\n        StagedSharedDict::new(prefix.to_owned(), columns).expect(\"StagedSharedDict succeed\"),\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use geo_types::Point;\n\n    use super::*;\n    use crate::Layer;\n    use crate::decoder::GeometryValues;\n    use crate::encoder::{Codecs, Encoder};\n    use crate::test_helpers::{dec, parser};\n\n    fn layer_tile(staged: StagedLayer) -> TileLayer {\n        let mut codecs = Codecs::default();\n        let buf = staged\n            .encode_into(Encoder::default(), &mut codecs)\n            .unwrap()\n            .into_layer_bytes()\n            .unwrap();\n        let (_, layer) = Layer::from_bytes(&buf, &mut parser()).unwrap();\n        let Layer::Tag01(lazy) = layer else { panic!() };\n        let mut d = dec();\n        lazy.decode_all(&mut d).unwrap().into_tile(&mut d).unwrap()\n    }\n\n    fn two_points() -> GeometryValues {\n        let mut g = GeometryValues::default();\n        g.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(0, 0)));\n        g.push_geom(&geo_types::Geometry::<i32>::Point(Point::new(1, 1)));\n        g\n    }\n\n    /// `into_tile` must produce a **typed** null (e.g. `PropValue::Bool(None)`)\n    /// for null slots, matching the column's actual type, even when the **first**\n    /// feature is null.\n    #[test]\n    fn null_first_feature_preserves_later_typed_value() {\n        let tile = layer_tile(StagedLayer {\n            name: \"t\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: two_points(),\n            properties: vec![StagedProperty::opt_bool(\"flag\", vec![None, Some(false)])],\n        });\n\n        assert_eq!(tile.property_names, vec![\"flag\"]);\n        // Null slot → typed null matching the column type\n        assert_eq!(tile.features[0].properties[0], PropValue::Bool(None));\n        // Non-null value after the null must not be dropped\n        assert_eq!(tile.features[1].properties[0], PropValue::Bool(Some(false)));\n    }\n\n    /// Every scalar type must produce a typed null for null slots and a typed\n    /// non-null value for present slots, even when the first feature is null.\n    #[test]\n    fn null_first_feature_across_types() {\n        let props = vec![\n            StagedProperty::opt_bool(\"b\", vec![None, Some(true)]),\n            StagedProperty::opt_i8(\"i8\", vec![None, Some(-1)]),\n            StagedProperty::opt_u8(\"u8\", vec![None, Some(2)]),\n            StagedProperty::opt_i32(\"i32\", vec![None, Some(-3)]),\n            StagedProperty::opt_u32(\"u32\", vec![None, Some(4)]),\n            StagedProperty::opt_i64(\"i64\", vec![None, Some(-5)]),\n            StagedProperty::opt_u64(\"u64\", vec![None, Some(6)]),\n            StagedProperty::opt_f32(\"f32\", vec![None, Some(7.0)]),\n            StagedProperty::opt_f64(\"f64\", vec![None, Some(8.0)]),\n            StagedProperty::opt_str(\"s\", vec![None, Some(\"ok\")]),\n        ];\n        let tile = layer_tile(StagedLayer {\n            name: \"t\".into(),\n            extent: 4096,\n            id: StagedId::None,\n            geometry: two_points(),\n            properties: props,\n        });\n\n        // Feature 0: every column is null → typed null for each column\n        let n = &tile.features[0].properties;\n        assert_eq!(n[0], PropValue::Bool(None));\n        assert_eq!(n[1], PropValue::I8(None));\n        assert_eq!(n[2], PropValue::U8(None));\n        assert_eq!(n[3], PropValue::I32(None));\n        assert_eq!(n[4], PropValue::U32(None));\n        assert_eq!(n[5], PropValue::I64(None));\n        assert_eq!(n[6], PropValue::U64(None));\n        assert_eq!(n[7], PropValue::F32(None));\n        assert_eq!(n[8], PropValue::F64(None));\n        assert_eq!(n[9], PropValue::Str(None));\n\n        // Feature 1: every column has its typed non-null value\n        let p = &tile.features[1].properties;\n        assert_eq!(p[0], PropValue::Bool(Some(true)));\n        assert_eq!(p[1], PropValue::I8(Some(-1)));\n        assert_eq!(p[2], PropValue::U8(Some(2)));\n        assert_eq!(p[3], PropValue::I32(Some(-3)));\n        assert_eq!(p[4], PropValue::U32(Some(4)));\n        assert_eq!(p[5], PropValue::I64(Some(-5)));\n        assert_eq!(p[6], PropValue::U64(Some(6)));\n        assert_eq!(p[7], PropValue::F32(Some(7.0)));\n        assert_eq!(p[8], PropValue::F64(Some(8.0)));\n        assert_eq!(p[9], PropValue::Str(Some(\"ok\".into())));\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/unknown.rs",
    "content": "use integer_encoding::VarIntWriter as _;\n\nuse crate::MltResult;\nuse crate::decoder::Unknown;\nuse crate::encoder::Encoder;\nuse crate::encoder::model::EncodedUnknown;\nuse crate::utils::{BinarySerializer as _, checked_sum2};\n\nimpl EncodedUnknown {\n    /// Serialize an unknown layer record directly to [`enc.data`](Encoder::data).\n    ///\n    /// Writes the complete `[varint(size)][tag][value]` record — the bytes are\n    /// already in wire format so no `hdr`/`meta` split is needed.\n    pub fn write_to(&self, mut enc: Encoder) -> MltResult<Encoder> {\n        let buffer_len = u32::try_from(self.value.len())?;\n        let size = checked_sum2(buffer_len, 1)?;\n        enc.write_varint(size)?;\n        enc.write_u8(self.tag)?;\n        enc.data.extend_from_slice(&self.value);\n        Ok(enc)\n    }\n}\n\nimpl<'a> From<Unknown<'a>> for EncodedUnknown {\n    fn from(u: Unknown<'a>) -> Self {\n        Self {\n            tag: u.tag,\n            value: u.value.to_vec(),\n        }\n    }\n}\n\n#[cfg(all(not(test), feature = \"arbitrary\"))]\nimpl arbitrary::Arbitrary<'_> for EncodedUnknown {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {\n        let mut tag: u8 = u.arbitrary()?;\n        // Tag 1 is the known Tag01 format; producing it as Unknown would break round-trip-ability\n        if tag == 1 {\n            tag = 0;\n        }\n        Ok(Self {\n            tag,\n            value: u.arbitrary()?,\n        })\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/encoder/writer.rs",
    "content": "use std::collections::HashMap;\nuse std::{io, mem};\n\nuse fsst::Compressor;\nuse integer_encoding::VarIntWriter as _;\n\nuse crate::decoder::{ColumnType, Morton};\nuse crate::encoder::model::{CurveParams, ExplicitEncoder, StrEncoding, StreamCtx};\nuse crate::encoder::{EncoderConfig, IntEncoder, VertexBufferType};\nuse crate::utils::BinarySerializer as _;\nuse crate::{MltError, MltResult};\n\n/// Stateful encoder that accumulates encoded layer bytes.\n///\n/// Logical temporary buffers live in `Codecs` and are passed alongside\n/// the encoder while a stream is being transformed and serialized. Physical\n/// encoders live here with their own scratch buffers, then copy complete\n/// payloads into [`data`](Encoder::data).\n///\n/// # Buffer layout\n///\n/// The MLT layer wire format is:\n///\n/// ```text\n/// [varint(body_len + 1)] [tag = 1]\n/// [name: string] [extent: varint] [column_count: varint]   <- hdr\n/// [col_type₁] [col_type₂] … [col_typeN]                    <- meta\n/// [col₁ stream data] [col₂ stream data] … [colN stream data] <- data\n/// ```\n///\n/// The three sections are accumulated into separate buffers so they can be\n/// combined at the end *without* any in-place insertion or extra copies:\n///\n/// * [`hdr`] – layer header (name, extent, `column_count`).\n/// * [`meta`] – column-type bytes (one byte + optional name per column).\n/// * [`data`] – encoded stream data; also the target of [`impl Write`].\n///\n/// # Sort-strategy trialing\n///\n/// Create one `Encoder` per sort-strategy trial, encode the layer into it,\n/// and keep the one whose `total_len()` is smallest:\n///\n/// ```rust,ignore\n/// let mut codecs = Codecs::default();\n/// let mut best: Option<Encoder> = None;\n/// for strategy in strategies {\n///     let mut enc = Encoder::new(cfg);\n///     layer.write_to(&mut enc, &mut codecs)?;\n///     if best.as_ref().is_none_or(|b| enc.total_len() < b.total_len()) {\n///         best = Some(enc);\n///     }\n/// }\n/// return best.unwrap().into_layer_bytes();\n/// ```\n///\n/// # Stream-level encoding alternatives\n///\n/// Use [`Encoder::try_alternatives`] to open a competition,\n/// then submit each candidate via `AltSession::with`.  The guard's `Drop`\n/// impl finalises the competition automatically:\n///\n/// ```rust,ignore\n/// let mut alt = enc.try_alternatives();\n/// alt.with(|enc| write_stream_as_varint(data, enc))?;\n/// alt.with(|enc| write_stream_as_fastpfor(data, enc))?;\n/// // alt drops → keeps whichever was shorter\n/// ```\n///\n/// [`hdr`]: Encoder::hdr\n/// [`meta`]: Encoder::meta\n/// [`data`]: Encoder::data\n/// [`impl Write`]: Encoder#impl-Write\n#[derive(Default)]\npub struct Encoder {\n    /// Encoding configuration: controls which optimization strategies are tried\n    /// (sort orders, compression algorithms, etc.).\n    ///\n    /// Set once at construction time via [`Encoder::new`]; propagated\n    /// automatically to all sub-encoders so individual encode methods do not\n    /// need a separate `cfg` argument.\n    pub cfg: EncoderConfig,\n\n    /// When [`Some`], property / ID / geometry encoders use `ExplicitEncoder`\n    /// callbacks instead of trying candidate encodings. When [`None`], the\n    /// automatic optimization path runs.\n    pub(crate) explicit: Option<ExplicitEncoder>,\n\n    /// Layer header bytes: `name`, `extent`, `column_count`.\n    ///\n    /// Written to `hdr` via [`Encoder::write_header`].  This section comes\n    /// first in the wire format and is never subject to alternatives.\n    pub hdr: Vec<u8>,\n\n    /// Column-type metadata bytes.\n    ///\n    /// Each column contributes one type byte (plus a name string for property\n    /// columns).  Written by the `write_columns_meta_to` methods, which write\n    /// directly to `enc.meta`.  This section comes second in the wire format\n    /// and is never subject to alternatives (column types are fixed).\n    pub meta: Vec<u8>,\n\n    /// Encoded stream data.\n    ///\n    /// All stream counts, per-stream encoding-metadata bytes, and encoded\n    /// data bytes land here via [`impl Write`].  This section comes last in\n    /// the wire format and is where stream-level alternatives compete.\n    ///\n    /// [`impl Write`]: Encoder#impl-Write\n    pub data: Vec<u8>,\n\n    /// Morton parameters for this layer's vertex set; `None` if the extent\n    /// exceeds 16 bits per axis (Morton encoding is unusable in that case).\n    /// Pre-populated by [`StagedLayer::encode_into`](crate::encoder::StagedLayer::encode_into).\n    pub(crate) morton_cache: Option<Morton>,\n\n    /// Hilbert curve parameters for this layer's vertex set. Pre-populated by\n    /// [`StagedLayer::encode_into`](crate::encoder::StagedLayer::encode_into).\n    pub(crate) hilbert_cache: Option<CurveParams>,\n\n    /// Cached FSST compressor per string column, keyed by column name.\n    /// `None` means training found FSST not viable for that column.\n    /// Trained on deduplicated values on the first sort trial, reused on subsequent trials.\n    pub(crate) fsst_cache: HashMap<String, Option<Compressor>>,\n\n    // -----------------------------------------------------------------------\n    // Alternatives state — a stack that supports nested competitions.\n    //\n    // Invariant between candidates at any level:\n    //   data.len() == level.data_start + level.best_data_size.unwrap_or(0)\n    //   meta.len() == level.meta_start + level.best_meta_size.unwrap_or(0)\n    //\n    // Empty stack ↔ no competition in progress.\n    // -----------------------------------------------------------------------\n    /// Stack of active encoding competitions, innermost last.\n    ///\n    /// Empty while no [`Encoder::try_alternatives`] session\n    /// is in progress.\n    alt_stack: Vec<AltLevel>,\n}\n\nimpl Encoder {\n    /// Create a new encoder with the given [`EncoderConfig`].\n    ///\n    /// Use [`Encoder::default()`] when the default configuration is sufficient.\n    #[inline]\n    #[must_use]\n    pub fn new(cfg: EncoderConfig) -> Self {\n        Self {\n            cfg,\n            ..Self::default()\n        }\n    }\n\n    /// Like [`Self::new`] but with the explicit encoder set for deterministic encoding\n    /// (tests, synthetics). Use with `StagedLayer::encode_explicit`.\n    #[inline]\n    #[must_use]\n    pub fn with_explicit(cfg: EncoderConfig, explicit: ExplicitEncoder) -> Self {\n        Self {\n            cfg,\n            explicit: Some(explicit),\n            ..Self::default()\n        }\n    }\n\n    /// Ensure this encoder is in the good state, and moves results to a new instance.\n    /// This allows current instance to be reused for other experiment, avoiding repeat of some operations.\n    #[must_use]\n    pub(crate) fn preserve_results(&mut self) -> Self {\n        assert_eq!(self.alt_stack.len(), 0, \"Alternatives stack is not empty\");\n        Self {\n            cfg: EncoderConfig::default(),\n            explicit: None,\n            hdr: mem::take(&mut self.hdr),\n            meta: mem::take(&mut self.meta),\n            data: mem::take(&mut self.data),\n            morton_cache: None,\n            hilbert_cache: None,\n            fsst_cache: HashMap::new(),\n            alt_stack: vec![],\n        }\n    }\n\n    #[inline]\n    pub(crate) fn write_column_type(&mut self, column_type: ColumnType) -> MltResult<()> {\n        column_type.write_to(&mut self.meta).map_err(MltError::from)\n    }\n\n    #[inline]\n    pub(crate) fn write_column_name(&mut self, name: &str) -> MltResult<()> {\n        self.meta.write_string(name).map_err(MltError::from)\n    }\n\n    #[inline]\n    pub(crate) fn write_column_header(\n        &mut self,\n        column_type: ColumnType,\n        name: &str,\n    ) -> MltResult<()> {\n        self.write_column_type(column_type)?;\n        self.write_column_name(name)\n    }\n\n    /// Write the layer header (`name`, `extent`, `column_count`) to [`hdr`].\n    ///\n    /// Must be called exactly once per layer, after all column meta and data.\n    ///\n    /// [`hdr`]: Encoder::hdr\n    #[hotpath::measure]\n    pub fn write_header(&mut self, name: &str, extent: u32, column_count: usize) -> MltResult<()> {\n        debug_assert!(\n            self.alt_stack.is_empty(),\n            \"write_header called with an open alternatives session\"\n        );\n        let name_len = u32::try_from(name.len())?;\n        let column_count = u32::try_from(column_count)?;\n        self.hdr.write_varint(name_len).map_err(MltError::from)?;\n        self.hdr.extend_from_slice(name.as_bytes());\n        self.hdr.write_varint(extent).map_err(MltError::from)?;\n        self.hdr\n            .write_varint(column_count)\n            .map_err(MltError::from)?;\n        Ok(())\n    }\n\n    /// When [`Self::explicit`] is [`Some`], returns the callback-chosen [`IntEncoder`].\n    /// [`None`] means run automatic candidate selection for that stream.\n    #[inline]\n    pub(crate) fn override_int_enc(&self, ctx: &StreamCtx<'_>) -> Option<IntEncoder> {\n        self.explicit.as_ref().map(|e| (e.get_int_encoder)(ctx))\n    }\n\n    /// When [`Self::explicit`] is [`Some`], returns the callback-chosen [`StrEncoding`].\n    /// [`None`] means run automatic string / shared-dict corpus selection.\n    #[inline]\n    pub(crate) fn override_str_enc(&self, name: &str) -> Option<StrEncoding> {\n        self.explicit.as_ref().map(|e| (e.get_str_encoding)(name))\n    }\n\n    /// Pinned vertex layout when an explicit encoder is active.\n    #[inline]\n    #[allow(clippy::unused_self)]\n    pub(crate) fn override_vertex_buffer_type(&self) -> Option<VertexBufferType> {\n        self.explicit.as_ref().map(|e| e.vertex_buffer_type)\n    }\n\n    /// Whether to force writing a geometry stream even when its data is empty.\n    ///\n    /// Delegates to [`ExplicitEncoder::force_stream`]; returns `false` when no explicit\n    /// encoder is active (the default \"skip empty streams\" behavior).\n    #[inline]\n    pub(crate) fn force_stream(&self, ctx: &StreamCtx<'_>) -> bool {\n        self.explicit\n            .as_ref()\n            .is_some_and(|e| (e.force_stream)(ctx))\n    }\n\n    /// Total encoded bytes across all three sections (`hdr + meta + data`).\n    #[inline]\n    #[must_use]\n    pub fn total_len(&self) -> usize {\n        self.hdr.len() + self.meta.len() + self.data.len()\n    }\n\n    /// Concatenate `hdr + meta + data` into a single buffer **without** a\n    /// tag/size prefix.\n    ///\n    /// Use this when the caller expects raw layer body bytes (without the size/tag framing)\n    /// rather than a complete framed wire record — see [`Self::into_layer_bytes`] for the framed form.\n    #[must_use]\n    pub fn into_raw_bytes(mut self) -> Vec<u8> {\n        let mut out = Vec::with_capacity(self.hdr.len() + self.meta.len() + self.data.len());\n        out.append(&mut self.hdr);\n        out.append(&mut self.meta);\n        out.append(&mut self.data);\n        out\n    }\n\n    /// Assemble the complete Tag-01 layer record.\n    pub fn into_layer_bytes(self) -> MltResult<Vec<u8>> {\n        self.into_layer_bytes_with_tag(1)\n    }\n\n    /// Assemble a complete layer record for the given `tag`:\n    /// `[varint(body_len + 1)][tag][hdr][meta][data]`.\n    fn into_layer_bytes_with_tag(mut self, tag: u8) -> MltResult<Vec<u8>> {\n        debug_assert!(\n            self.alt_stack.is_empty(),\n            \"into_layer_bytes_with_tag called with an open alternatives session\"\n        );\n        let body_len = self.hdr.len() + self.meta.len() + self.data.len();\n        let size = u32::try_from(body_len + 1)?; // +1 for the tag byte\n        let mut out = Vec::with_capacity(5 + 1 + body_len);\n        out.write_varint(size).map_err(MltError::from)?;\n        out.push(tag);\n        out.append(&mut self.hdr);\n        out.append(&mut self.meta);\n        out.append(&mut self.data);\n        Ok(out)\n    }\n\n    /// Begin a new encoding competition.\n    ///\n    /// Returns an `AltSession` guard.  Submit each candidate via\n    /// `AltSession::with`; the guard's `Drop` impl finalises\n    /// the competition and retains the shortest candidate automatically.\n    ///\n    /// Nesting is supported: calling `try_alternatives` inside a\n    /// `with` closure opens an inner competition on the same stack,\n    /// resolved before the outer candidate is committed.\n    ///\n    /// # Example\n    ///\n    /// ```rust,ignore\n    /// let mut alt = enc.try_alternatives();\n    /// for cand in candidates {\n    ///     alt.with(|enc| write_candidate(cand, enc))?;\n    /// }\n    /// // alt drops → finalises the competition\n    /// ```\n    pub fn try_alternatives(&mut self) -> AltSession<'_> {\n        self.alt_stack.push(AltLevel {\n            data_start: self.data.len(),\n            meta_start: self.meta.len(),\n            best_data: None,\n            best_meta: None,\n        });\n        AltSession { enc: self }\n    }\n\n    /// Commit the current candidate at the innermost competition level.\n    ///\n    /// Compares bytes written since the last commit against the running best\n    /// by **total** (`data + meta`) size; keeps the shorter one.\n    ///\n    /// Called internally by `AltSession::with` on `Ok`.\n    fn alt_commit(&mut self) {\n        debug_assert!(\n            !self.alt_stack.is_empty(),\n            \"alt_commit called outside an active AltSession\"\n        );\n        let (data, meta, stack) = (&mut self.data, &mut self.meta, &mut self.alt_stack);\n        let level = stack.last_mut().unwrap();\n        Self::close_candidate(data, meta, level);\n    }\n\n    /// Finalize the innermost competition and pop it from the stack.\n    ///\n    /// Any bytes written since the last `alt_commit` are evaluated as a\n    /// final candidate; if no pending bytes exist and a best is already\n    /// recorded this is a cheap stack-pop.\n    fn alt_pop(&mut self) {\n        debug_assert!(\n            !self.alt_stack.is_empty(),\n            \"alt_pop called outside an active AltSession\"\n        );\n        {\n            let (data, meta, stack) = (&mut self.data, &mut self.meta, &mut self.alt_stack);\n            let level = stack.last_mut().unwrap();\n            let data_pending = data.len() - (level.data_start + level.best_data.unwrap_or(0));\n            let meta_pending = meta.len() - (level.meta_start + level.best_meta.unwrap_or(0));\n            if data_pending > 0 || meta_pending > 0 || level.best_data.is_none() {\n                Self::close_candidate(data, meta, level);\n            }\n        }\n        self.alt_stack.pop();\n    }\n\n    /// Shared compare-and-keep logic used by both `alt_commit` and `alt_pop`.\n    ///\n    /// Compares the bytes written since the last committed candidate against\n    /// the current best by **total** (`data + meta`) size.\n    /// Keeps the shorter one; ties preserve the existing best.\n    fn close_candidate(data: &mut Vec<u8>, meta: &mut Vec<u8>, level: &mut AltLevel) {\n        let best_data_end = level.data_start + level.best_data.unwrap_or(0);\n        let best_meta_end = level.meta_start + level.best_meta.unwrap_or(0);\n        let cand_data = data.len() - best_data_end;\n        let cand_meta = meta.len() - best_meta_end;\n        let cand_total = cand_data + cand_meta;\n        let best_total = level.best_data.unwrap_or(0) + level.best_meta.unwrap_or(0);\n        if level.best_data.is_none_or(|_| cand_total < best_total) {\n            // New best: shift data candidate bytes to data_start.\n            if level.best_data.is_some() {\n                data.copy_within(best_data_end..best_data_end + cand_data, level.data_start);\n                meta.copy_within(best_meta_end..best_meta_end + cand_meta, level.meta_start);\n            }\n            data.truncate(level.data_start + cand_data);\n            meta.truncate(level.meta_start + cand_meta);\n            level.best_data = Some(cand_data);\n            level.best_meta = Some(cand_meta);\n        } else {\n            // Not an improvement: discard.\n            data.truncate(best_data_end);\n            meta.truncate(best_meta_end);\n        }\n    }\n}\n\n/// State for one level of an encoding competition.\n///\n/// Tracks the starting position in both the [`data`](Encoder::data) and\n/// [`meta`](Encoder::meta) buffers, and the byte count of the best candidate\n/// committed so far.\n///\n/// Candidates are compared by **total** bytes (`data + meta`); the shorter one\n/// wins, with ties resolved in favor of the earlier candidate.\n#[derive(Debug, Default, Clone)]\nstruct AltLevel {\n    data_start: usize,\n    meta_start: usize,\n    /// Byte count appended to `data` by the current best candidate.\n    best_data: Option<usize>,\n    /// Byte count appended to `meta` by the current best candidate.\n    best_meta: Option<usize>,\n}\n\n/// RAII guard for a stream-encoding competition opened by [`Encoder::try_alternatives`].\n///\n/// Submit each candidate via [`with`](AltSession::with); on `Ok` the candidate is\n/// committed (compared against the running best and kept if shorter); on `Err`\n/// the partial write is rolled back and the error propagates.  The guard's\n/// `Drop` impl finalises the competition automatically, so the [`Encoder`] is\n/// always left in a consistent state even when an error exits the loop early.\n///\n/// Nesting is allowed: calling [`Encoder::try_alternatives`] inside a\n/// `with` closure opens an inner competition that is fully\n/// resolved before the outer candidate is committed.\n#[must_use = \"AltSession must be used; drop it to finalise the competition\"]\npub struct AltSession<'a> {\n    enc: &'a mut Encoder,\n}\n\nimpl AltSession<'_> {\n    /// Encode one candidate.\n    ///\n    /// - **`Ok`** — commits the candidate; replaces the running best if shorter.\n    /// - **`Err`** — truncates the partial write back to the pre-call checkpoint\n    ///   and returns the error.  The guard's `Drop` still finalises the\n    ///   competition cleanly using whichever candidates succeeded so far.\n    #[hotpath::measure]\n    pub fn with<F>(&mut self, f: F) -> MltResult<()>\n    where\n        F: FnOnce(&mut Encoder) -> MltResult<()>,\n    {\n        let data_cp = self.enc.data.len();\n        let meta_cp = self.enc.meta.len();\n        match f(self.enc) {\n            Ok(()) => {\n                self.enc.alt_commit();\n                Ok(())\n            }\n            Err(e) => {\n                self.enc.data.truncate(data_cp);\n                self.enc.meta.truncate(meta_cp);\n                Err(e)\n            }\n        }\n    }\n}\n\nimpl Drop for AltSession<'_> {\n    fn drop(&mut self) {\n        self.enc.alt_pop();\n    }\n}\n\n/// Writes bytes to [`Encoder::data`].\n///\n/// This blanket implementation makes `Encoder` compatible with all\n/// `BinarySerializer`, `VarIntWriter`, and other `Write`-based utilities so that\n/// stream-data methods do not need a separate code path.\nimpl io::Write for Encoder {\n    #[inline]\n    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n        self.data.write(buf)\n    }\n\n    #[inline]\n    fn flush(&mut self) -> io::Result<()> {\n        Ok(())\n    }\n\n    #[inline]\n    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {\n        self.data.write_all(buf)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    /// Helper: directly extend `enc.data` with raw bytes (simulates a stream write).\n    fn push(enc: &mut Encoder, bytes: &[u8]) {\n        enc.data.extend_from_slice(bytes);\n    }\n\n    // ── basic single-level behavior ──────────────────────────────────────\n\n    /// The shortest candidate wins.\n    #[test]\n    fn alternatives_keeps_shortest() {\n        let mut enc = Encoder::default();\n        push(&mut enc, b\"prefix\");\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"longer\");\n            Ok(())\n        })\n        .unwrap(); // 6 bytes\n        alt.with(|enc| {\n            push(enc, b\"ab\");\n            Ok(())\n        })\n        .unwrap(); // 2 bytes — shortest\n        alt.with(|enc| {\n            push(enc, b\"xyz\");\n            Ok(())\n        })\n        .unwrap(); // 3 bytes\n        drop(alt);\n\n        assert_eq!(enc.data, b\"prefixab\");\n    }\n\n    /// On a tie the first candidate is kept (strict `<`, not `<=`).\n    #[test]\n    fn alternatives_tie_keeps_first() {\n        let mut enc = Encoder::default();\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"aaa\");\n            Ok(())\n        })\n        .unwrap(); // 3 bytes\n        alt.with(|enc| {\n            push(enc, b\"bbb\");\n            Ok(())\n        })\n        .unwrap(); // 3 bytes — equal\n        drop(alt);\n\n        assert_eq!(enc.data, b\"aaa\");\n    }\n\n    /// A single candidate is unconditionally the winner.\n    #[test]\n    fn alternatives_single_candidate() {\n        let mut enc = Encoder::default();\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"only\");\n            Ok(())\n        })\n        .unwrap();\n        drop(alt);\n\n        assert_eq!(enc.data, b\"only\");\n    }\n\n    /// Bytes written before `try_alternatives` are left intact throughout.\n    #[test]\n    fn prefix_bytes_are_preserved() {\n        let mut enc = Encoder::default();\n        push(&mut enc, b\"HDR\");\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"long_encoding\");\n            Ok(())\n        })\n        .unwrap(); // 13 bytes\n        alt.with(|enc| {\n            push(enc, b\"short\");\n            Ok(())\n        })\n        .unwrap(); // 5 bytes — winner\n        drop(alt);\n\n        assert_eq!(&enc.data[..3], b\"HDR\");\n        assert_eq!(&enc.data[3..], b\"short\");\n    }\n\n    /// Dropping the guard after all candidates are committed is a cheap stack-pop.\n    #[test]\n    fn drop_after_all_committed_is_noop() {\n        let mut enc = Encoder::default();\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"best\");\n            Ok(())\n        })\n        .unwrap();\n        drop(alt); // all candidates committed; drop just pops the stack\n\n        assert!(enc.alt_stack.is_empty(), \"stack empty after drop\");\n        assert_eq!(enc.data, b\"best\");\n    }\n\n    // ── nesting ───────────────────────────────────────────────────────────\n\n    /// An inner competition is resolved before the outer candidate is committed.\n    #[test]\n    fn nested_alternatives() {\n        let mut enc = Encoder::default();\n\n        let mut outer = enc.try_alternatives();\n\n        // Outer candidate A: header bytes + inner competition.\n        outer\n            .with(|enc| {\n                push(enc, b\"A:\");\n                let mut inner = enc.try_alternatives(); // inner level pushed\n                inner.with(|enc| {\n                    push(enc, b\"long_inner\");\n                    Ok(())\n                })?; // 10 bytes\n                inner.with(|enc| {\n                    push(enc, b\"in\");\n                    Ok(())\n                })?; // 2 bytes — inner winner\n                drop(inner); // inner done; enc = b\"A:in\"\n                push(enc, b\"!\");\n                Ok(())\n            })\n            .unwrap(); // outer candidate A = b\"A:in!\" (5 bytes)\n\n        // Outer candidate B: shorter overall.\n        outer\n            .with(|enc| {\n                push(enc, b\"B\");\n                Ok(())\n            })\n            .unwrap(); // 1 byte — winner\n        drop(outer);\n\n        assert_eq!(enc.data, b\"B\");\n    }\n\n    /// Stack depth tracks nesting level; inner guard drops before outer closure returns.\n    #[test]\n    fn nesting_depth_reflected_in_stack() {\n        let mut enc = Encoder::default();\n\n        assert_eq!(enc.alt_stack.len(), 0);\n        let mut outer = enc.try_alternatives();\n\n        outer\n            .with(|enc| {\n                assert_eq!(enc.alt_stack.len(), 1); // outer level on stack\n                let mut inner = enc.try_alternatives();\n                inner.with(|enc| {\n                    assert_eq!(enc.alt_stack.len(), 2); // both levels on stack\n                    push(enc, b\"x\");\n                    Ok(())\n                })?;\n                drop(inner); // inner popped\n                assert_eq!(enc.alt_stack.len(), 1);\n                push(enc, b\"y\");\n                Ok(())\n            })\n            .unwrap();\n\n        drop(outer); // outer popped\n        assert_eq!(enc.alt_stack.len(), 0);\n    }\n\n    // ── meta buffer tracking ──────────────────────────────────────────────\n\n    /// Writes to both `data` and `meta` are rolled back for the losing\n    /// candidate and kept for the winner, measured by total bytes.\n    #[test]\n    fn alternatives_tracks_meta_and_data() {\n        let mut enc = Encoder::default();\n        enc.data.extend_from_slice(b\"D\");\n        enc.meta.extend_from_slice(b\"M\");\n\n        let mut alt = enc.try_alternatives();\n        // Candidate A: 4 data + 2 meta = 6 total\n        alt.with(|enc| {\n            push(enc, b\"DDDD\");\n            enc.meta.extend_from_slice(b\"mm\");\n            Ok(())\n        })\n        .unwrap();\n        // Candidate B: 1 data + 1 meta = 2 total — winner\n        alt.with(|enc| {\n            push(enc, b\"d\");\n            enc.meta.extend_from_slice(b\"n\");\n            Ok(())\n        })\n        .unwrap();\n        drop(alt);\n\n        assert_eq!(enc.data, b\"Dd\");\n        assert_eq!(enc.meta, b\"Mn\");\n    }\n\n    // ── error rollback ────────────────────────────────────────────────────\n\n    /// A failing candidate is rolled back; prior best is preserved.\n    #[test]\n    fn error_candidate_is_rolled_back() {\n        let mut enc = Encoder::default();\n\n        let mut alt = enc.try_alternatives();\n        alt.with(|enc| {\n            push(enc, b\"ok\");\n            Ok(())\n        })\n        .unwrap();\n        let _ = alt.with(|enc| {\n            push(enc, b\"partial\");\n            Err(MltError::IntegerOverflow) // simulated failure\n        });\n        drop(alt);\n\n        assert_eq!(enc.data, b\"ok\"); // \"partial\" was rolled back; \"ok\" kept\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/errors.rs",
    "content": "use std::convert::Infallible;\nuse std::num::TryFromIntError;\n\nuse num_enum::TryFromPrimitiveError;\n\nuse crate::decoder::{\n    GeometryType, LogicalEncoding, LogicalTechnique, PhysicalEncoding, StreamType,\n};\nuse crate::utils::AsUsize;\n\npub type MltResult<T> = Result<T, MltError>;\npub(crate) type MltRefResult<'a, T> = Result<(&'a [u8], T), MltError>;\n\n#[derive(Debug, thiserror::Error)]\n#[non_exhaustive]\npub enum MltError {\n    #[error(\"cannot decode {0} as {1}\")]\n    DataWidthMismatch(&'static str, &'static str),\n    #[error(\"dictionary index {0} out of bounds (len={1})\")]\n    DictIndexOutOfBounds(u32, usize),\n    #[error(\"duplicate value found where unique required\")]\n    DuplicateValue,\n    #[error(\"Integer overflow\")]\n    IntegerOverflow,\n    #[error(\"missing geometry column in feature table\")]\n    MissingGeometry,\n    #[error(\"missing string stream: {0}\")]\n    MissingStringStream(&'static str),\n    #[error(\"multiple geometry columns found (only one allowed)\")]\n    MultipleGeometryColumns,\n    #[error(\"multiple ID columns found (only one allowed)\")]\n    MultipleIdColumns,\n    #[error(\"varint uses more bytes than necessary (non-canonical encoding)\")]\n    NonCanonicalVarInt,\n    #[error(\"{0} is not decoded\")]\n    NotDecoded(&'static str),\n    #[error(\"decoded data is not in encoded form\")]\n    NotEncoded,\n    #[error(\"error parsing column type: code={0}\")]\n    ParsingColumnType(u8),\n    #[error(\"error parsing logical technique: code={0}\")]\n    ParsingLogicalTechnique(u8),\n    #[error(\"error parsing physical encoding: code={0}\")]\n    ParsingPhysicalEncoding(u8),\n    #[error(\"error parsing stream type: code={0}\")]\n    ParsingStreamType(u8),\n    #[error(\"found {0} bytes after the expected end of layer\")]\n    TrailingLayerData(usize),\n    #[error(\"unexpected end of input (unable to take {0} bytes)\")]\n    UnableToTake(u32),\n    #[error(\"unexpected stream type {0:?}\")]\n    UnexpectedStreamType(StreamType),\n    #[error(\"unexpected stream type {0:?}, expected {1} for {2}\")]\n    UnexpectedStreamType2(StreamType, &'static str, &'static str),\n    #[error(\"unsupported logical encoding {0:?} for {1}\")]\n    UnsupportedLogicalEncoding(LogicalEncoding, &'static str),\n    #[error(\"invalid combination of logical encodings: {0:?} + {1:?}\")]\n    InvalidLogicalEncodings(LogicalTechnique, LogicalTechnique),\n    #[error(\"layer has zero size\")]\n    ZeroLayerSize,\n    #[error(\"The encoder used to optimise data is incompatible\")]\n    BadEncoderDataCombination,\n    #[error(\"StagedLayer::encode_explicit requires Encoder.explicit to be Some(_)\")]\n    MissingExplicitEncoder,\n\n    // Wire/codec decoding (bytes → primitives)\n    #[error(\"buffer underflow: needed {0} bytes, but only {1} remain\")]\n    BufferUnderflow(u32, usize),\n    #[error(\"FastPFor decode failed: expected={0} got={1}\")]\n    FastPforDecode(u32, usize),\n    #[error(\"invalid RLE run length (cannot convert to usize): value={0}\")]\n    RleRunLenInvalid(i128),\n\n    // Structural constraints (lengths, counts, shapes)\n    #[error(\"geometry requires at least 1 stream, got 0\")]\n    GeometryWithoutStreams,\n    #[error(\"FastPFor data byte length expected multiple of 4, got {0}\")]\n    InvalidFastPforByteLength(usize),\n    #[error(\"vec2 delta stream size expected to be non-empty and multiple of 2, got {0}\")]\n    InvalidPairStreamSize(usize),\n    #[error(\"decodable stream size expected {1}, got {0}\")]\n    InvalidDecodingStreamSize(usize, usize),\n    #[error(\"IDs missing for encoding (expected Some IDs, got None)\")]\n    IdsMissingForEncoding,\n    #[error(\"missing struct encoder for struct\")]\n    MissingStructEncoderForStruct,\n    #[error(\"previous decode/parsing attempt failed\")]\n    PriorParseFailure,\n    #[error(\"presence stream has {0} bits set but {1} values provided\")]\n    PresenceValueCountMismatch(usize, usize),\n    #[error(\"MVT parse error: {0}\")]\n    MvtParse(String),\n    #[error(\"need to encode before being able to write\")]\n    NeedsEncodingBeforeWriting,\n    #[error(\"memory limit exceeded: limit={limit}, used={used}, requested={requested}\")]\n    MemoryLimitExceeded {\n        limit: u32,\n        used: u32,\n        requested: u32,\n    },\n    #[error(\"not implemented: {0}\")]\n    NotImplemented(&'static str),\n    #[error(\"unsupported property value and encoder combination: {0:?} + {1:?}\")]\n    UnsupportedPropertyEncoderCombination(&'static str, &'static str),\n    #[error(\"mixed property types are not allowed in column {0} ({1})\")]\n    MixedPropertyTypes(usize, String),\n    #[error(\"shared dictionary requires at least 2 streams, got {0}\")]\n    SharedDictRequiresStreams(usize),\n    #[error(\"unsupported string stream count (expected between 2 and 5): {0}\")]\n    UnsupportedStringStreamCount(usize),\n    #[error(\"Structs are not allowed to be optional\")]\n    TriedToEncodeOptionalStruct,\n    #[error(\n        \"encoding instruction count mismatch: expected {input_len} instructions for {input_len} properties, got {config_len}\"\n    )]\n    EncodingInstructionCountMismatch { input_len: usize, config_len: usize },\n    #[error(\"struct child data streams expected exactly 1 value, got {0}\")]\n    UnexpectedStructChildCount(u32),\n    // Note that {expected}+1 is allowed for the legacy Java encoder bug\n    #[error(\"SharedDict stream count is {actual}, expected {expected}\")]\n    InvalidSharedDictStreamCount { actual: u32, expected: u32 },\n    #[error(\"unsupported physical encoding: {0}\")]\n    UnsupportedPhysicalEncoding(&'static str),\n    #[error(\"unsupported physical encoding: {0:?} for {1}\")]\n    UnsupportedPhysicalEncodingForType(PhysicalEncoding, &'static str),\n    #[error(\n        \"Extent {extent} cannot be encoded to morton due to morton allowing max. 16 bits, but {required_bits} would be required\"\n    )]\n    VertexMortonNotCompatibleWithExtent { extent: u32, required_bits: u32 },\n    #[error(\"Morton stream uses {0} bits, but at most 16 bits are supported\")]\n    InvalidMortonBits(u32),\n\n    // Geometry decode errors (field = variable name, geom_type for context)\n    #[error(\"MVT error: {0}\")]\n    BadMvtGeometry(&'static str),\n    #[error(\"geometry[{0}]: index out of bounds\")]\n    GeometryIndexOutOfBounds(usize),\n    #[error(\"geometry[{index}]: {field}[{idx}] out of bounds (len={len})\")]\n    GeometryOutOfBounds {\n        index: usize,\n        field: &'static str,\n        idx: usize,\n        len: usize,\n    },\n    #[error(\"geometry[{index}]: vertex {vertex} out of bounds (count={count})\")]\n    GeometryVertexOutOfBounds {\n        index: usize,\n        vertex: usize,\n        count: usize,\n    },\n    #[error(\"geometry[{0}]: {1} requires geometry_offsets\")]\n    NoGeometryOffsets(usize, GeometryType),\n    #[error(\"geometry[{0}]: {1} requires part_offsets\")]\n    NoPartOffsets(usize, GeometryType),\n    #[error(\"geometry[{0}]: {1} requires ring_offsets\")]\n    NoRingOffsets(usize, GeometryType),\n    #[error(\"geometry[{0}]: unexpected offset combination for {1}\")]\n    UnexpectedOffsetCombination(usize, GeometryType),\n\n    #[error(\"FastPFor error: {0}\")]\n    FastPfor(#[from] fastpfor::FastPForError),\n    #[error(transparent)]\n    Io(#[from] std::io::Error),\n    #[error(\"Serde JSON error: {0}\")]\n    SerdeJsonError(#[from] serde_json::Error),\n    #[error(\"integer conversion error: {0}\")]\n    TryFromIntError(#[from] TryFromIntError),\n    #[error(\"num_enum conversion error: {0}\")]\n    TryFromPrimitive(#[from] TryFromPrimitiveError<GeometryType>),\n    #[error(\"UTF-8 decode error: {0}\")]\n    Utf8(#[from] std::str::Utf8Error),\n    #[error(\"UTF-8 decode error: {0}\")]\n    FromUtf8(#[from] std::string::FromUtf8Error),\n}\n\nimpl From<Infallible> for MltError {\n    fn from(_: Infallible) -> Self {\n        unreachable!()\n    }\n}\n\nimpl From<MltError> for std::io::Error {\n    fn from(value: MltError) -> Self {\n        match value {\n            MltError::Io(e) => e,\n            other => Self::other(other),\n        }\n    }\n}\n\npub(crate) trait AsMltError<T> {\n    fn or_overflow(&self) -> MltResult<T>;\n}\n\nimpl<T: Copy> AsMltError<T> for Option<T> {\n    #[inline]\n    fn or_overflow(&self) -> MltResult<T> {\n        self.ok_or(MltError::IntegerOverflow)\n    }\n}\n\nimpl AsMltError<u32> for Result<u32, TryFromIntError> {\n    #[inline]\n    fn or_overflow(&self) -> MltResult<u32> {\n        self.map_err(|_| MltError::IntegerOverflow)\n    }\n}\n\n#[inline]\npub(crate) fn fail_if_invalid_stream_size<T: AsUsize>(actual: T, expected: T) -> MltResult<()> {\n    if actual == expected {\n        Ok(())\n    } else {\n        Err(MltError::InvalidDecodingStreamSize(\n            actual.as_usize(),\n            expected.as_usize(),\n        ))\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\nextern crate core;\n\n/// Validates stream metadata in constructors (crate-internal).\nmacro_rules! validate_stream {\n    ($stream:expr, $expected:pat $(,)?) => {\n        if !matches!($stream.meta.stream_type, $expected) {\n            return Err($crate::MltError::UnexpectedStreamType2(\n                $stream.meta.stream_type,\n                stringify!($expected),\n                stringify!($stream),\n            ));\n        }\n    };\n}\n\n// re-export geo types\npub use geo_types;\n\npub(crate) mod codecs;\npub(crate) mod convert;\npub(crate) mod decoder;\npub mod encoder;\npub(crate) mod errors;\npub(crate) mod utils;\n\npub use convert::{geojson, mvt};\npub use decoder::{\n    ColumnRef, Decoder, FeatureRef, GeometryType, GeometryValues, Layer, Layer01,\n    Layer01FeatureIter, LendingIterator, ParsedLayer, ParsedLayer01, Parser, PropName, PropValue,\n    PropValueRef, TileFeature, TileLayer, Unknown,\n};\n// Crate-internal re-exports: allow internal modules to use `crate::Lazy` etc.\n// without exposing these implementation details to external users.\npub(crate) use decoder::{\n    ColumnType, DictRange, DictionaryType, LengthType, OffsetType, RawPresence, RawSharedDict,\n    RawSharedDictItem, StreamType,\n};\npub(crate) use errors::MltRefResult;\npub use errors::{MltError, MltResult};\npub(crate) use utils::analyze::{Analyze, StatType};\npub(crate) use utils::lazy_state::{Decode, DecodeState, Lazy, LazyParsed, Parsed};\n\n/// Wire-level encoding metadata — for tile analysis and tooling.\n///\n/// These types describe the physical and logical encoding of streams inside an\n/// MLT tile. Normal tile consumers (parse → iterate features) do not need this\n/// module; it is intended for tools that inspect or report encoding statistics.\npub mod wire {\n    pub use crate::decoder::ColumnType;\n    pub use crate::decoder::stream::model::{\n        DictionaryType, IntEncoding, LengthType, LogicalEncoding, LogicalTechnique, Morton,\n        OffsetType, PhysicalEncoding, RleMeta, StreamMeta, StreamType,\n    };\n    pub use crate::utils::analyze::{Analyze, StatType};\n}\n\n#[cfg(any(test, feature = \"__private\"))]\npub use crate::utils::test_helpers;\n\n/// Private re-exports for benchmarks and integration tests. Not part of the public API.\n#[cfg(any(test, feature = \"__private\"))]\n#[doc(hidden)]\npub mod __private {\n    pub use crate::codecs::*;\n    pub use crate::convert::*;\n    pub use crate::decoder::*;\n    pub use crate::errors::*;\n    pub use crate::test_helpers::*;\n    pub use crate::utils::analyze::*;\n    pub use crate::utils::lazy_state::*;\n    pub use crate::utils::*;\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/analyze.rs",
    "content": "use std::ops::Deref;\n\nuse enum_dispatch::enum_dispatch;\n\nuse crate::LazyParsed;\nuse crate::decoder::{ParsedProperty, ParsedScalar, ParsedSharedDict, ParsedStrings, StreamMeta};\n\n/// What to calculate with [`Analyze::collect_statistic`].\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum StatType {\n    /// Geometry/Feature/id data size in bytes (excludes metadata overhead).\n    DecodedDataSize,\n    /// Metadata overhead in bytes (stream headers, names, extent, geometry types).\n    DecodedMetaSize,\n    /// Number of features (geometry entries).\n    FeatureCount,\n}\n\n/// Trait for estimating various size/count metrics.\n#[enum_dispatch]\npub trait Analyze {\n    fn collect_statistic(&self, _stat: StatType) -> usize {\n        0\n    }\n\n    /// Call `cb` with the [`StreamMeta`] of every stream contained in `self`.\n    /// Default implementation is a no-op (types that hold no streams).\n    fn for_each_stream(&self, _cb: &mut dyn FnMut(StreamMeta)) {}\n}\n\nmacro_rules! impl_statistics_fixed {\n    ($($ty:ty),+) => {\n        $(impl Analyze for $ty {\n            fn collect_statistic(&self, _stat: StatType) -> usize {\n                size_of::<$ty>()\n            }\n        }\n        impl Analyze for &[$ty] {\n            fn collect_statistic(&self, _stat: StatType) -> usize {\n                size_of::<$ty>() * self.len()\n            }\n        })+\n    };\n}\n\nimpl_statistics_fixed!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64);\n\nimpl Analyze for String {\n    fn collect_statistic(&self, _stat: StatType) -> usize {\n        self.len()\n    }\n}\n\nimpl<T: Analyze> Analyze for Option<T> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        self.as_ref().map_or(0, |v| v.collect_statistic(stat))\n    }\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        if let Some(v) = self {\n            v.for_each_stream(cb);\n        }\n    }\n}\n\nimpl<T: Analyze> Analyze for [T] {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        self.iter().map(|v| v.collect_statistic(stat)).sum()\n    }\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        for v in self {\n            v.for_each_stream(cb);\n        }\n    }\n}\n\nimpl<T: Analyze> Analyze for Vec<T> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        self.as_slice().collect_statistic(stat)\n    }\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        self.as_slice().for_each_stream(cb);\n    }\n}\n\n/// Opt-in marker for blanket `Analyze` delegation via `Deref`.\n///\n/// A type that implements both `Deref<Target = T>` (with `T: Analyze`) and this\n/// marker trait automatically receives an `Analyze` impl that delegates every call\n/// to the dereferenced value.\npub(crate) trait AnalyzeViaDeref {}\n\nimpl<T: Analyze, R: Deref<Target = T> + AnalyzeViaDeref> Analyze for R {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        (**self).collect_statistic(stat)\n    }\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        (**self).for_each_stream(cb);\n    }\n}\n\nimpl<Raw: Analyze, Parsed: Analyze> Analyze for LazyParsed<Raw, Parsed> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        match self {\n            Self::Raw(encoded) => encoded.collect_statistic(stat),\n            Self::Parsed(decoded) => decoded.collect_statistic(stat),\n            Self::ParsingFailed => 0,\n        }\n    }\n\n    fn for_each_stream(&self, cb: &mut dyn FnMut(StreamMeta)) {\n        match self {\n            Self::Raw(encoded) => encoded.for_each_stream(cb),\n            Self::Parsed(decoded) => decoded.for_each_stream(cb),\n            Self::ParsingFailed => {}\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/extensions.rs",
    "content": "use num_traits::CheckedAdd;\n\nuse crate::errors::AsMltError as _;\nuse crate::{MltError, MltResult};\n\npub trait SetOptionOnce<T> {\n    fn set_once(&mut self, value: T) -> MltResult<()>;\n}\n\nimpl<T> SetOptionOnce<T> for Option<T> {\n    fn set_once(&mut self, value: T) -> MltResult<()> {\n        if self.replace(value).is_some() {\n            Err(MltError::DuplicateValue)\n        } else {\n            Ok(())\n        }\n    }\n}\n\npub trait AsUsize: Eq + Copy {\n    fn as_usize(&self) -> usize;\n}\n\nimpl AsUsize for usize {\n    #[inline]\n    fn as_usize(&self) -> usize {\n        *self\n    }\n}\n\nimpl AsUsize for u32 {\n    #[inline]\n    fn as_usize(&self) -> usize {\n        const _: () = {\n            // Some day Rust may support usize smaller than u32?\n            assert!(\n                size_of::<u32>() <= size_of::<usize>(),\n                \"usize must be able to hold all u32 values\"\n            );\n        };\n        usize::try_from(*self).unwrap()\n    }\n}\n\n/// Perform checked addition of two values, returning an error if any overflow occurs.\n#[inline]\npub fn checked_sum2<T: CheckedAdd + Copy>(v1: T, v2: T) -> MltResult<T> {\n    v1.checked_add(&v2).or_overflow()\n}\n\n/// Perform checked addition of three values, returning an error if any overflow occurs.\n#[inline]\npub fn checked_sum3<T: CheckedAdd + Copy>(v1: T, v2: T, v3: T) -> MltResult<T> {\n    v1.checked_add(&v2)\n        .and_then(|sum| sum.checked_add(&v3))\n        .or_overflow()\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/formatter.rs",
    "content": "use std::fmt::{self, Debug, Display, Formatter};\n\nuse hex::ToHex as _;\n\nfn format_byte_array(data: &[u8]) -> String {\n    let vals = data.encode_hex_upper::<String>();\n    format!(\"[0x{vals}; {}]\", data.len())\n}\n\n/// derive-debug formatter for `&[u8]` fields.\npub fn bytes_dbg(data: &&[u8]) -> String {\n    format_byte_array(data)\n}\n\nfn format_opt_seq<T: Display>(v: Option<&[T]>) -> String {\n    match v {\n        None => \"None\".to_string(),\n        Some(v) => {\n            let items = v\n                .iter()\n                .map(ToString::to_string)\n                .collect::<Vec<_>>()\n                .join(\",\");\n            format!(\"[{items}; {}]\", v.len())\n        }\n    }\n}\n\n/// derive-debug formatter for `Vec<T>` fields.\npub fn vec_seq<T: Display>(v: &[T]) -> String {\n    format_opt_seq(Some(v))\n}\n\n/// derive-debug formatter for `Option<Vec<T>>` fields.\n#[allow(clippy::ref_option, reason = \"called by Dbg codegen\")]\npub fn opt_vec_seq<T: Display>(v: &Option<Vec<T>>) -> String {\n    format_opt_seq(v.as_deref())\n}\n\n/// Wraps any `Debug` value and formats it in compact (non-alternate) mode.\n/// Used to prevent inner types from expanding to multiple lines in `{:#?}` output.\npub struct CompactDbg<T>(T);\n\nimpl<T: Debug> Display for CompactDbg<T> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        write!(f, \"{:?}\", self.0)\n    }\n}\n\n/// derive-debug formatter that forces compact `{:?}` output regardless of alternate mode.\npub fn compact_dbg<T: Debug>(t: &T) -> CompactDbg<&T> {\n    CompactDbg(t)\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/lazy_state.rs",
    "content": "use std::mem;\n\nuse crate::{Decoder, MltError, MltResult};\n\npub trait Decode<Parsed>: Sized {\n    fn decode(self, decoder: &mut Decoder) -> MltResult<Parsed>;\n}\n\nmod sealed {\n    pub trait Sealed {}\n}\n\n/// Type-state marker for [`Layer01`](crate::Layer01) and related column wrappers.\n///\n/// Implementors determine how `(Raw, Parsed)` column pairs are stored:\n/// - [`Lazy`] stores an [`LazyParsed<Raw, Parsed>`] enum that can be in `Raw`, `Parsed`, or `ParsingFailed` state.\n/// - [`Parsed`] stores only `Parsed`, giving zero-cost infallible field access.\npub trait DecodeState: sealed::Sealed {\n    type LazyOrParsed<Raw, Parsed>;\n}\n\n/// Lazy state: individual columns may still be raw or already decoded.\n///\n/// This is the default state returned by [`Parser::parse_layers`](crate::Parser::parse_layers).\n/// All columns can be decoded at once by calling [`Layer01::decode_all`](crate::Layer01::decode_all),\n/// which consumes `self` and returns a [`Layer01<Parsed>`](crate::Layer01).\n#[derive(Debug, Clone, PartialEq)]\npub struct Lazy;\n\n/// Fully-decoded state: all columns hold their parsed values directly.\n///\n/// A `Layer01<Parsed>` is produced by [`Layer01::decode_all`](crate::Layer01::decode_all).\n/// Its fields (`id`, `geometry`, `properties`) are the parsed types themselves — no\n/// wrapping enum, no `Result`, just plain field access.\n#[derive(Debug, Clone, PartialEq)]\npub struct Parsed;\n\nimpl sealed::Sealed for Lazy {}\nimpl sealed::Sealed for Parsed {}\n\nimpl DecodeState for Lazy {\n    type LazyOrParsed<Raw, Parsed> = LazyParsed<Raw, Parsed>;\n}\nimpl DecodeState for Parsed {\n    /// In the decoded state the column IS the parsed value — no enum wrapper.\n    type LazyOrParsed<Raw, Parsed> = Parsed;\n}\n\n/// Shared wrapper for values that may still be in the original (raw) format or\n/// already parsed (but still columnar).\n/// Used by: `Id`, `Geometry`, `Property`, and eventually - `SharedDictItem`\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug, PartialEq, Clone)]\n#[cfg_attr(all(not(test), feature = \"arbitrary\"), derive(arbitrary::Arbitrary))]\npub enum LazyParsed<Raw, Parsed> {\n    Raw(Raw),\n    Parsed(Parsed),\n    ParsingFailed,\n}\n\nimpl<Raw: Decode<Parsed>, Parsed> LazyParsed<Raw, Parsed> {\n    /// Decode in place, replacing the raw value with the parsed result.\n    pub fn decode(&mut self, decoder: &mut Decoder) -> MltResult<&mut Parsed> {\n        match self {\n            Self::Parsed(v) => Ok(v),\n            Self::Raw(_) => {\n                let Self::Raw(raw) = mem::replace(self, Self::ParsingFailed) else {\n                    unreachable!();\n                };\n                *self = Self::Parsed(raw.decode(decoder)?);\n                let Self::Parsed(v) = self else {\n                    unreachable!()\n                };\n                Ok(v)\n            }\n            Self::ParsingFailed => Err(MltError::PriorParseFailure),\n        }\n    }\n\n    /// Consume and return the parsed value, decoding if currently raw.\n    pub fn into_parsed(self, decoder: &mut Decoder) -> MltResult<Parsed> {\n        match self {\n            Self::Parsed(v) => Ok(v),\n            Self::Raw(raw) => raw.decode(decoder),\n            Self::ParsingFailed => Err(MltError::PriorParseFailure),\n        }\n    }\n\n    pub fn as_parsed(&self) -> MltResult<&Parsed> {\n        match self {\n            Self::Parsed(v) => Ok(v),\n            Self::Raw(_) => Err(MltError::NotDecoded(\"enc_dec value\")), // TODO: I wonder if the str can be of the type name?\n            Self::ParsingFailed => Err(MltError::PriorParseFailure),\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/mod.rs",
    "content": "pub(crate) mod analyze;\npub(crate) mod extensions;\npub(crate) mod formatter;\npub(crate) mod lazy_state;\nmod parse;\npub mod presence;\nmod serialize;\n#[cfg(any(test, feature = \"__private\"))]\npub mod test_helpers;\n\npub use extensions::*;\npub(crate) use parse::*;\npub use presence::*;\npub use serialize::*;\n"
  },
  {
    "path": "rust/mlt-core/src/utils/parse.rs",
    "content": "use crate::codecs::varint::parse_varint;\nuse crate::utils::Presence;\nuse crate::{Decoder, MltError, MltRefResult, MltResult, RawPresence};\n\n#[inline]\npub fn take(input: &[u8], size: u32) -> MltRefResult<'_, &[u8]> {\n    let (value, input) = input\n        .split_at_checked(size.try_into()?)\n        .ok_or(MltError::UnableToTake(size))?;\n    Ok((input, value))\n}\n\n/// Parse a length-prefixed UTF-8 string from the input\npub fn parse_string(input: &[u8]) -> MltRefResult<'_, &str> {\n    let (input, length) = parse_varint::<u32>(input)?;\n    let (input, value) = take(input, length)?;\n    let value = str::from_utf8(value)?;\n    Ok((input, value))\n}\n\n/// Parse a single byte from the input\npub fn parse_u8(input: &[u8]) -> MltRefResult<'_, u8> {\n    if input.is_empty() {\n        Err(MltError::UnableToTake(1))\n    } else {\n        Ok((&input[1..], input[0]))\n    }\n}\n\n/// Decode an optional presence stream, combining it with the dense values.\n///\n/// Returns [`Presence::AllPresent`] wrapping `values` when `presence.0` is `None`\n/// (non-optional column). Otherwise decodes the bitvector and checks that the\n/// number of set bits equals `values.len()` (the number of non-null values already decoded).\npub fn decode_presence<'a, T: Copy>(\n    presence: RawPresence<'a>,\n    values: Vec<T>,\n    dec: &mut Decoder,\n) -> MltResult<Presence<'a, T>> {\n    let Some(raw) = presence.0 else {\n        return Ok(Presence::AllPresent(values));\n    };\n    let bits = raw.decode_bitvec(dec)?;\n    let set_count = bits.count_ones();\n    let dense_count = values.len();\n    if set_count != dense_count {\n        return Err(MltError::PresenceValueCountMismatch(set_count, dense_count));\n    }\n    Ok(Presence::Bits { bits, values })\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/presence.rs",
    "content": "use std::borrow::Cow;\n\nuse bitvec::order::Lsb0;\nuse bitvec::slice::BitSlice;\n\nuse crate::{Analyze, StatType};\n\n/// Per-column feature presence bitvector paired with its dense values.\n///\n/// Bit order matches the wire format (`bitvec`'s `Lsb0`): bit `i` corresponds to\n/// `(byte[i/8] >> (i%8)) & 1`.\n#[derive(Clone, PartialEq, Debug)]\npub enum Presence<'a, T: Copy> {\n    /// No presence stream — every feature has a value.\n    AllPresent(Vec<T>),\n    /// Per-feature packed bitvector: bit `i` is set iff feature `i` has a value.\n    /// `values` holds only the non-null (present) entries in dense order.\n    Bits {\n        bits: Cow<'a, BitSlice<u8, Lsb0>>,\n        values: Vec<T>,\n    },\n}\n\nimpl<T: Copy> Presence<'_, T> {\n    /// Returns `true` if feature `idx` is present, `false` if absent or out of bounds.\n    #[inline]\n    #[must_use]\n    pub fn is_present(&self, idx: usize) -> bool {\n        match self {\n            Self::AllPresent(values) => idx < values.len(),\n            Self::Bits { bits, .. } => bits.get(idx).as_deref().copied().unwrap_or(false),\n        }\n    }\n\n    /// Total number of features (present and absent).\n    #[inline]\n    #[must_use]\n    pub fn feature_count(&self) -> usize {\n        match self {\n            Self::AllPresent(values) => values.len(),\n            Self::Bits { bits, .. } => bits.len(),\n        }\n    }\n\n    /// Dense values slice (present entries only).\n    #[inline]\n    #[must_use]\n    pub fn dense_values(&self) -> &[T] {\n        match self {\n            Self::AllPresent(values) | Self::Bits { values, .. } => values,\n        }\n    }\n\n    /// Returns the value for feature `idx`, or `None` if absent or out of bounds.\n    ///\n    /// For sequential access over all features prefer [`Presence::iter_optional`],\n    /// which is O(1) per step. This method recomputes `count_ones()` each call and\n    /// is O(idx) for sparse (Bits) presence.\n    #[inline]\n    #[must_use]\n    pub fn get(&self, idx: usize) -> Option<T> {\n        match self {\n            Self::AllPresent(values) => values.get(idx).copied(),\n            Self::Bits { bits, values } => {\n                if *bits.get(idx)? {\n                    Some(values[bits[..idx].count_ones()])\n                } else {\n                    None\n                }\n            }\n        }\n    }\n\n    /// Expand into a `Vec<Option<T>>` with one entry per feature.\n    ///\n    /// Allocates; prefer [`Presence::get`] for single-feature access or\n    /// [`Presence::iter_optional`] for sequential access without allocation.\n    #[must_use]\n    pub fn materialize(&self) -> Vec<Option<T>> {\n        self.iter_optional().collect()\n    }\n\n    /// Iterate over all features in order, yielding `Option<T>` per feature in O(1) per step.\n    ///\n    /// Unlike repeated [`Presence::get`] calls (which are O(idx) for sparse columns),\n    /// this iterator tracks `dense_idx` internally and advances in O(1) per step.\n    #[must_use]\n    pub fn iter_optional(&self) -> PresenceOptIter<'_, T> {\n        match self {\n            Self::AllPresent(values) => PresenceOptIter {\n                bits: None,\n                values,\n                feat_idx: 0,\n                dense_idx: 0,\n            },\n            Self::Bits { bits, values } => PresenceOptIter {\n                bits: Some(bits),\n                values,\n                feat_idx: 0,\n                dense_idx: 0,\n            },\n        }\n    }\n}\n\nimpl<T: Analyze + Copy> Analyze for Presence<'_, T> {\n    fn collect_statistic(&self, stat: StatType) -> usize {\n        if stat == StatType::DecodedMetaSize {\n            0\n        } else {\n            let bits_size = match self {\n                Self::AllPresent(_) => 0,\n                Self::Bits { bits, .. } => bits.len().div_ceil(8),\n            };\n            bits_size + self.dense_values().collect_statistic(stat)\n        }\n    }\n}\n\n/// O(1)-per-step iterator over all features of a [`Presence`], yielding `Option<T>`.\n///\n/// Returned by [`Presence::iter_optional`]. Prefer this over repeated [`Presence::get`]\n/// calls when iterating in order: `get` is O(idx) for sparse columns (recomputes\n/// `count_ones()`), while this iterator advances in O(1) per step by tracking\n/// `dense_idx` internally.\npub struct PresenceOptIter<'p, T: Copy> {\n    /// `None` for `AllPresent`, `Some(bits)` for `Bits`.\n    bits: Option<&'p BitSlice<u8, Lsb0>>,\n    values: &'p [T],\n    feat_idx: usize,\n    dense_idx: usize,\n}\n\nimpl<T: Copy> Iterator for PresenceOptIter<'_, T> {\n    type Item = Option<T>;\n\n    fn next(&mut self) -> Option<Option<T>> {\n        match self.bits {\n            None => {\n                let v = self.values.get(self.feat_idx).copied()?;\n                self.feat_idx += 1;\n                Some(Some(v))\n            }\n            Some(bits) => {\n                if self.feat_idx >= bits.len() {\n                    return None;\n                }\n                let present = bits[self.feat_idx];\n                self.feat_idx += 1;\n                if present {\n                    let v = self.values[self.dense_idx];\n                    self.dense_idx += 1;\n                    Some(Some(v))\n                } else {\n                    Some(None)\n                }\n            }\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let remaining = match self.bits {\n            None => self.values.len().saturating_sub(self.feat_idx),\n            Some(bits) => bits.len().saturating_sub(self.feat_idx),\n        };\n        (remaining, Some(remaining))\n    }\n}\n\nimpl<T: Copy> ExactSizeIterator for PresenceOptIter<'_, T> {}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/serialize.rs",
    "content": "use std::io;\nuse std::io::Write;\n\nuse integer_encoding::VarIntWriter;\n\n#[cfg(any(test, feature = \"__private\"))]\nuse crate::encoder::EncodedStream;\nuse crate::{MltError, MltResult};\n\npub trait BinarySerializer: Write + VarIntWriter + Sized {\n    fn write_u8(&mut self, value: u8) -> io::Result<()> {\n        self.write_all(&[value])\n    }\n    fn write_string(&mut self, value: &str) -> io::Result<()> {\n        let size = u32::try_from(value.len()).map_err(MltError::from)?;\n        self.write_varint(size)?;\n        self.write_all(value.as_bytes())\n    }\n\n    /// Reverses `RawStream::from_bytes` — writes a stream header then the stream data bytes.\n    #[cfg(any(test, feature = \"__private\"))]\n    fn write_stream(&mut self, stream: &EncodedStream) -> io::Result<()> {\n        let byte_length = u32::try_from(stream.data.len()).map_err(MltError::from)?;\n        stream.meta.write_to(self, false, byte_length)?;\n        stream.write_to(self)?;\n        Ok(())\n    }\n\n    /// Reverses `RawStream::parse_bool` — writes a boolean stream header then the stream data bytes.\n    #[cfg(test)]\n    fn write_boolean_stream(&mut self, stream: &EncodedStream) -> io::Result<()> {\n        let byte_length = u32::try_from(stream.data.len()).map_err(MltError::from)?;\n        stream.meta.write_to(self, true, byte_length)?;\n        stream.write_to(self)?;\n        Ok(())\n    }\n}\n\nimpl<T> BinarySerializer for T where T: Write + VarIntWriter {}\n\npub fn strings_to_lengths<S: AsRef<str>>(values: &[S]) -> MltResult<Vec<u32>> {\n    Ok(values\n        .iter()\n        .map(|s| u32::try_from(s.as_ref().len()))\n        .collect::<Result<Vec<_>, _>>()?)\n}\n"
  },
  {
    "path": "rust/mlt-core/src/utils/test_helpers.rs",
    "content": "//! Shared helpers for unit tests, integration tests, and benchmarks.\n\nuse crate::decoder::Layer01;\nuse crate::{Decoder, Layer, MltRefResult, Parser};\n\n/// Default decoder for decoding in tests.\n#[must_use]\npub fn dec() -> Decoder {\n    Decoder::default()\n}\n\n/// Default parser for parsing in tests.\n#[must_use]\npub fn parser() -> Parser {\n    Parser::default()\n}\n\npub fn assert_empty<T>(result: MltRefResult<T>) -> T {\n    let (remaining, value) = result.unwrap();\n    assert!(remaining.is_empty(), \"{} bytes remain\", remaining.len());\n    value\n}\n\n#[must_use]\npub fn into_layer01(layer: Layer) -> Layer01 {\n    match layer {\n        Layer::Tag01(layer01) => layer01,\n        Layer::Unknown(_) => panic!(\"expected Tag01 layer\"),\n    }\n}\n"
  },
  {
    "path": "rust/mlt-core/tests/geojson.rs",
    "content": "use std::fs;\nuse std::path::Path;\n\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::test_helpers::{dec, parser};\nuse test_each_file::test_each_path;\n\ntest_each_path! { for [\"mlt\"] in \"../test/expected/tag0x01\" as geojson => geojson_test }\n\nfn geojson_test([mlt]: [&Path; 1]) {\n    let buffer = fs::read(mlt).unwrap();\n    let mut p = parser();\n    let layers = p.parse_layers(&buffer).unwrap();\n    assert!(p.reserved() > 0);\n\n    let mut d = dec();\n    let decoded = d.decode_all(layers).unwrap();\n    assert!(d.consumed() > 0);\n    let fc = FeatureCollection::from_layers(decoded).unwrap();\n    assert!(!fc.features.is_empty(), \"expected at least one feature\");\n}\n"
  },
  {
    "path": "rust/mlt-core/tests/snapshots.rs",
    "content": "#![allow(unused)]\n\nuse std::ffi::OsString;\nuse std::fs;\nuse std::path::Path;\n\nuse insta::{assert_debug_snapshot, assert_snapshot, with_settings};\nuse mlt_core::test_helpers::{dec, parser};\nuse test_each_file::test_each_path;\n\n//\n// ATTENTION:  this test is likely to be deleted soon.\n//             Geometry validation is done via `mlt ls --validate-to-json`.\n//             See the CLI integration tests / CI configuration for details.\n\n// test_each_path! { for [\"mlt\"] in \"../test/expected/tag0x01\" as parse => parse }\n\nfn parse([path]: [&Path; 1]) {\n    let mut snapshot_path = OsString::from(\"snapshots-\");\n    snapshot_path.push(path.parent().unwrap().file_name().unwrap());\n    with_settings! {\n        { omit_expression => true,\n          snapshot_path => snapshot_path ,\n          prepend_module_to_snapshot => false },\n        {\n            parse_one_file(path);\n        }\n    }\n}\n\n/// Parse a single MLT file and assert a snapshot of the result.\nfn parse_one_file(path: impl AsRef<Path>) {\n    let path = path.as_ref();\n    eprintln!(\"Parsing MLT file: {}\", path.display());\n    let file_name = path.file_stem().unwrap().to_string_lossy().to_string();\n    let buffer = fs::read(path).unwrap();\n    let mut p = parser();\n    let mut d = dec();\n    match p.parse_layers(&buffer) {\n        Ok(layers) => {\n            assert_snapshot!(p.reserved(), @\"\");\n            assert_debug_snapshot!(format!(\"{file_name}-parsed\"), layers);\n            let mut decoded = Vec::with_capacity(layers.len());\n            for (idx, layer) in layers.into_iter().enumerate() {\n                match layer.decode_all(&mut d) {\n                    Ok(l) => decoded.push(l),\n                    Err(e) => {\n                        let idx = if idx == 0 {\n                            String::new()\n                        } else {\n                            format!(\"_{idx}\")\n                        };\n                        assert_debug_snapshot!(format!(\"{file_name}{idx}___bad-decode\"), e);\n                        break;\n                    }\n                }\n            }\n            assert_snapshot!(d.consumed(), @\"\");\n            assert_debug_snapshot!(format!(\"{file_name}-decoded\"), decoded);\n        }\n        Err(e) => {\n            let filesize = buffer.len();\n            assert_debug_snapshot!(format!(\"{file_name}___bad___{filesize}\"), e);\n        }\n    }\n}\n\n// test_each_path! { for [\"mlt\"] in \"../test/expected/tag0x01\" as decode => decode }\n// fn decode([path]: [&Path; 1]) {\n//     let buffer = fs::read(path).unwrap();\n//     let mut layers = parse_layers(&buffer).expect(\"MLT file parse\");\n//     for layer in &mut layers {\n//         match layer.decode_all() {\n//             Ok(v) => {\n//                 assert_debug_snapshot!(path.as_str(), v);\n//             }\n//             Err(e) => {\n//                 // assert_debug_snapshot!(format!(\"{file_name}___bad-decode\"), _e);\n//                 todo!(\"handle decode error: {e:#?}\");\n//             }\n//         }\n//     }\n// }\n\n#[test]\n#[ignore = \"used for manual testing of a single file\"]\nfn test_plain() {\n    // let path = \"../../test/expected/tag0x01/simple/line-boolean.mlt\";\n    let path = \"../../test/expected/tag0x01/omt/11_1062_1368.mlt\";\n    // let path = \"../../test/expected/tag0x01/omt/11_1062_1368.mlt\";\n    // let path = \"../../test/expected/tag0x01/bing/6-32-21.mlt\";\n\n    let path = Path::new(path);\n    let buffer = fs::read(path).unwrap();\n    let mut p = parser();\n    let layers = p.parse_layers(&buffer).unwrap();\n    assert_snapshot!(p.reserved(), @\"\");\n    let _ = layers;\n    // decode([&path]);\n}\n"
  },
  {
    "path": "rust/mlt-core/tests/unknown_layer.rs",
    "content": "/// Integration tests for [`mlt_core::Unknown`] public accessors.\n///\n/// Builds a minimal hand-crafted byte buffer that contains one unknown layer\n/// (tag = 42, body = [1, 2, 3]) and verifies that `Parser::parse_layers` yields\n/// a `Layer::Unknown` whose `tag()` and `data()` return the expected values.\n///\n/// Wire format of a single layer:\n/// ```text\n///   [varint: body_len_including_tag_byte] [tag_byte] [body_bytes...]\n///\n///   For tag=42, body=[1,2,3]:\n///     body_len = 1 (tag byte) + 3 (body bytes) = 4\n///     bytes = [0x04, 0x2A, 0x01, 0x02, 0x03]\n/// ```\nuse mlt_core::{Layer, Parser};\n\nfn unknown_layer_bytes(tag: u8, body: &[u8]) -> Vec<u8> {\n    // size varint = 1 (tag byte) + body.len()\n    let size = (1 + body.len()) as u64;\n\n    let mut buf = Vec::new();\n    // encode size as varint\n    let mut v = size;\n    loop {\n        let byte = (v & 0x7F) as u8;\n        v >>= 7;\n        if v == 0 {\n            buf.push(byte);\n            break;\n        }\n        buf.push(byte | 0x80);\n    }\n    buf.push(tag);\n    buf.extend_from_slice(body);\n    buf\n}\n\n#[test]\nfn unknown_tag_and_data_are_accessible() {\n    let body: &[u8] = &[1, 2, 3];\n    let raw = unknown_layer_bytes(42, body);\n\n    let layers = Parser::default()\n        .parse_layers(&raw)\n        .expect(\"parse should succeed\");\n\n    assert_eq!(layers.len(), 1);\n\n    let Layer::Unknown(u) = &layers[0] else {\n        panic!(\"expected Layer::Unknown, got {:?}\", layers[0]);\n    };\n\n    assert_eq!(u.tag(), 42u32);\n    assert_eq!(u.data(), body);\n}\n\n#[test]\nfn unknown_zero_length_body() {\n    let raw = unknown_layer_bytes(99, &[]);\n\n    let layers = Parser::default()\n        .parse_layers(&raw)\n        .expect(\"parse should succeed\");\n\n    let Layer::Unknown(u) = &layers[0] else {\n        panic!(\"expected Layer::Unknown\");\n    };\n\n    assert_eq!(u.tag(), 99u32);\n    assert!(u.data().is_empty());\n}\n\n#[test]\nfn multiple_layers_mixed_unknown_and_tag01() {\n    // Build two unknown layers back-to-back (tags 2 and 3, since tag=1 is Tag01).\n    let mut raw = unknown_layer_bytes(2, b\"hello\");\n    raw.extend_from_slice(&unknown_layer_bytes(3, b\"world\"));\n\n    let layers = Parser::default()\n        .parse_layers(&raw)\n        .expect(\"parse should succeed\");\n\n    assert_eq!(layers.len(), 2);\n\n    let Layer::Unknown(u0) = &layers[0] else {\n        panic!(\"expected Unknown at index 0\");\n    };\n    assert_eq!(u0.tag(), 2u32);\n    assert_eq!(u0.data(), b\"hello\");\n\n    let Layer::Unknown(u1) = &layers[1] else {\n        panic!(\"expected Unknown at index 1\");\n    };\n    assert_eq!(u1.tag(), 3u32);\n    assert_eq!(u1.data(), b\"world\");\n}\n"
  },
  {
    "path": "rust/mlt-py/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.1.12](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.11...python-mlt-v0.1.12) - 2026-04-29\n\n### Other\n\n- *(rust)* follow up to Presence cleanup ([#1337](https://github.com/maplibre/maplibre-tile-spec/pull/1337))\n\n## [0.1.11](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.10...python-mlt-v0.1.11) - 2026-04-18\n\n### Other\n\n- *(rust)* Coord32 cleanup, dep update ([#1304](https://github.com/maplibre/maplibre-tile-spec/pull/1304))\n\n## [0.1.10](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.9...python-mlt-v0.1.10) - 2026-04-13\n\n### Other\n\n- update Cargo.toml dependencies\n\n## [0.1.9](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.8...python-mlt-v0.1.9) - 2026-04-13\n\n### Added\n\n- *(rust)* enhance property iteration ([#1225](https://github.com/maplibre/maplibre-tile-spec/pull/1225))\n\n### Other\n\n- *(rust)* move frames/v01 -> decoder, adj use ([#1246](https://github.com/maplibre/maplibre-tile-spec/pull/1246))\n- *(rust)* mv tessellation to core ([#1220](https://github.com/maplibre/maplibre-tile-spec/pull/1220))\n- *(rust)* implement feature/property iterator and more type state ([#1198](https://github.com/maplibre/maplibre-tile-spec/pull/1198))\n- *(rust)* type state to represent fully-decoded layers ([#1171](https://github.com/maplibre/maplibre-tile-spec/pull/1171))\n- *(rust)* cleanup Results, minor styling ([#1192](https://github.com/maplibre/maplibre-tile-spec/pull/1192))\n\n## [0.1.8](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.7...python-mlt-v0.1.8) - 2026-03-23\n\n### Other\n\n- *(rust)* migrate to Rust fastpfor ([#1190](https://github.com/maplibre/maplibre-tile-spec/pull/1190))\n\n## [0.1.7](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.6...python-mlt-v0.1.7) - 2026-03-17\n\n### Other\n\n- *(rust)* memory budgeting, codecs  ([#1168](https://github.com/maplibre/maplibre-tile-spec/pull/1168))\n- *(rust)* introduce EncDec decode states ([#1166](https://github.com/maplibre/maplibre-tile-spec/pull/1166))\n- *(rust)* rename EncDec variants ([#1165](https://github.com/maplibre/maplibre-tile-spec/pull/1165))\n- *(rust)* add stateful decoder ([#1163](https://github.com/maplibre/maplibre-tile-spec/pull/1163))\n- *(rust)* rename to IdValues and GeometryValues ([#1159](https://github.com/maplibre/maplibre-tile-spec/pull/1159))\n- *(chore)* remove into_static, test fixes ([#1154](https://github.com/maplibre/maplibre-tile-spec/pull/1154))\n- *(rust)* refactor parsing and encoding code ([#1144](https://github.com/maplibre/maplibre-tile-spec/pull/1144))\n- *(rust)* simplify ID model ([#1139](https://github.com/maplibre/maplibre-tile-spec/pull/1139))\n- *(rust)* move name into DecodedStrings ([#1108](https://github.com/maplibre/maplibre-tile-spec/pull/1108))\n\n## [0.1.6](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.5...python-mlt-v0.1.6) - 2026-03-10\n\n### Other\n\n- *(rust)* rework our internal data model ([#1099](https://github.com/maplibre/maplibre-tile-spec/pull/1099))\n- *(rust)* move all structs to structs.rs ([#1094](https://github.com/maplibre/maplibre-tile-spec/pull/1094))\n\n## [0.1.5](https://github.com/maplibre/maplibre-tile-spec/compare/python-mlt-v0.1.4...python-mlt-v0.1.5) - 2026-03-08\n\n### Other\n\n- *(rust)* major rework of the shared dict ([#1066](https://github.com/maplibre/maplibre-tile-spec/pull/1066))\n\n## [0.1.4](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-pyo3-v0.1.3...python-mlt-v0.1.4) - 2026-03-04\n\n### Other\n\n- *(rust)* rework string parsing ([#1026](https://github.com/maplibre/maplibre-tile-spec/pull/1026))\n\n## [0.1.3](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-pyo3-v0.1.2...mlt-pyo3-v0.1.3) - 2026-02-25\n\n### Other\n\n- *(python)* Pyo3 type stub generation ([#966](https://github.com/maplibre/maplibre-tile-spec/pull/966))\n\n## [0.1.2](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-pyo3-v0.1.1...mlt-pyo3-v0.1.2) - 2026-02-25\n\n### Other\n\n- updated the following local packages: mlt-core\n\n## [0.1.1](https://github.com/maplibre/maplibre-tile-spec/compare/mlt-pyo3-v0.1.0...mlt-pyo3-v0.1.1) - 2026-02-25\n\n### Added\n\n- *(rust)* add qgis plugin for MLT ([#886](https://github.com/maplibre/maplibre-tile-spec/pull/886))\n"
  },
  {
    "path": "rust/mlt-py/Cargo.toml",
    "content": "[package]\nname = \"mlt-py\"\ndescription = \"Python bindings for MapLibre Tile (MLT) format via PyO3\"\nversion = \"0.1.12\"\nreadme = \"README.md\"\nrepository.workspace = true\nedition.workspace = true\nlicense.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\nrust-version.workspace = true\n\n[lib]\nname = \"mlt_py\"\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[[bin]]\nname = \"stub_gen\"\npath = \"src/bins/stub_gen.rs\"\nrequired-features = [\"abi3\"]\n\n[features]\nabi3 = [\"pyo3/abi3-py310\", \"generate-import-lib\"]\ngenerate-import-lib = [\"pyo3/generate-import-lib\"]\n\n[dependencies]\nmlt-core.workspace = true\npyo3.workspace = true\npyo3-stub-gen.workspace = true\nserde_json.workspace = true\n\n[dev-dependencies]\npyo3 = { workspace = true, features = [\"auto-initialize\"] }\n"
  },
  {
    "path": "rust/mlt-py/README.md",
    "content": "# mlt_py\n\nPython bindings for the MapLibre Tile (MLT) format via [PyO3](https://pyo3.rs/).\n\n```python\nimport maplibre_tiles\n\ndata = open(\"tile.mlt\", \"rb\").read()\n\n# Structured decode with geo-referencing\nlayers = maplibre_tiles.decode_mlt(data, z=14, x=8297, y=10749, tms=True)\nfor layer in layers:\n    print(f\"Layer: {layer.name}, extent: {layer.extent}\")\n    for feat in layer.features[:3]:\n        print(f\"  id={feat.id}, type={feat.geometry_type}\")\n        print(f\"  wkb={len(feat.wkb)} bytes, props={dict(feat.properties)}\")\n\n# Raw tile-local coordinates (no z/x/y needed)\nlayers = maplibre_tiles.decode_mlt(data)\n\n# GeoJSON string output (tile-local coords)\ngeojson_str = maplibre_tiles.decode_mlt_to_geojson(data)\n\n# Fast layer listing (no full decode)\nnames = maplibre_tiles.list_layers(data)\n```\n"
  },
  {
    "path": "rust/mlt-py/maplibre_tiles.pyi",
    "content": "# This file is automatically generated by pyo3_stub_gen\n# ruff: noqa: E501, F401, F403, F405\n\nimport builtins\nimport typing\n__all__ = [\n    \"MltFeature\",\n    \"MltLayer\",\n    \"decode_mlt\",\n    \"decode_mlt_to_geojson\",\n    \"list_layers\",\n]\n\n@typing.final\nclass MltFeature:\n    r\"\"\"\n    A decoded MLT feature with geometry, id, and properties.\n    \"\"\"\n    @property\n    def id(self) -> typing.Optional[builtins.int]: ...\n    @property\n    def geometry_type(self) -> builtins.str: ...\n    @property\n    def wkb(self) -> bytes: ...\n    @property\n    def properties(self) -> dict: ...\n    def __repr__(self) -> builtins.str: ...\n\n@typing.final\nclass MltLayer:\n    r\"\"\"\n    A decoded MLT layer containing features.\n    \"\"\"\n    @property\n    def name(self) -> builtins.str: ...\n    @property\n    def extent(self) -> builtins.int: ...\n    @property\n    def features(self) -> builtins.list[MltFeature]: ...\n    def __repr__(self) -> builtins.str: ...\n\ndef decode_mlt(data: bytes, z: typing.Optional[builtins.int] = None, x: typing.Optional[builtins.int] = None, y: typing.Optional[builtins.int] = None, tms: builtins.bool = True) -> builtins.list[MltLayer]:\n    r\"\"\"\n    Decode an MLT binary blob into a list of `MltLayer` objects.\n\n    If `z`, `x`, `y` are provided, tile-local coordinates are transformed\n    to EPSG:3857 (Web Mercator) meters. Without them, raw tile coordinates\n    are preserved.\n\n    `tms`: when True (the default), treat `y` as TMS convention (y=0 at south,\n    used by OpenMapTiles / MBTiles). Set to False for XYZ / slippy-map tiles\n    (y=0 at north, e.g. OSM raster tiles).\n    \"\"\"\n\ndef decode_mlt_to_geojson(data: bytes) -> builtins.str:\n    r\"\"\"\n    Decode an MLT binary blob and return GeoJSON as a string.\n    \"\"\"\n\ndef list_layers(data: bytes) -> builtins.list[builtins.str]:\n    r\"\"\"\n    Return a list of layer names without fully decoding.\n    \"\"\"\n"
  },
  {
    "path": "rust/mlt-py/pyproject.toml",
    "content": "[build-system]\nrequires = [\"maturin>=1.7,<2.0\"]\nbuild-backend = \"maturin\"\n\n[project]\nname = \"maplibre-tiles\"\nversion = \"0.1.12\"\ndescription = \"Python bindings for MapLibre Tile (MLT) format\"\nrequires-python = \">=3.10\"\nlicense = { text = \"MIT OR Apache-2.0\" }\nkeywords = [\"maplibre\", \"tile\", \"mlt\", \"vector-tiles\", \"gis\"]\nreadme = \"README.md\"\nclassifiers = [\n    \"License :: OSI Approved :: MIT License\",\n    \"License :: OSI Approved :: Apache Software License\",\n    \"Development Status :: 3 - Alpha\",\n    \"Intended Audience :: Developers\",\n    \"Intended Audience :: Science/Research\",\n    \"Intended Audience :: Information Technology\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"Programming Language :: Python :: Implementation :: CPython\",\n    \"Programming Language :: Rust\",\n    \"Operating System :: OS Independent\",\n    \"Topic :: Scientific/Engineering :: GIS\",\n    \"Topic :: Multimedia :: Graphics\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n    \"Topic :: Software Development :: Libraries\",\n]\n\n[project.urls]\nHomepage = \"https://maplibre.org/maplibre-tile-spec\"\nDocumentation = \"https://maplibre.org/maplibre-tile-spec\"\nRepository = \"https://github.com/maplibre/maplibre-tile-spec.git\"\nIssues = \"https://github.com/maplibre/maplibre-tile-spec/issues\"\nChangelog = \"https://github.com/maplibre/maplibre-tile-spec/blob/master/rust/mlt-py/CHANGELOG.md\"\n\n[tool.maturin]\nmanifest-path = \"Cargo.toml\"\nmodule-name = \"maplibre_tiles\"\n"
  },
  {
    "path": "rust/mlt-py/src/bins/stub_gen.rs",
    "content": "use pyo3_stub_gen::Result;\n\n/// purely a helper bin to generate the type stubs by CI\nfn main() -> Result<()> {\n    let stub = mlt_py::stub_info()?;\n    stub.generate()?;\n    Ok(())\n}\n"
  },
  {
    "path": "rust/mlt-py/src/feature.rs",
    "content": "use pyo3::types::{PyBytes, PyDict};\nuse pyo3::{Py, pyclass, pymethods};\nuse pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};\n\n/// A decoded MLT feature with geometry, id, and properties.\n#[gen_stub_pyclass]\n#[pyclass]\npub struct MltFeature {\n    #[pyo3(get)]\n    id: Option<u64>,\n    #[pyo3(get)]\n    geometry_type: String,\n    #[pyo3(get)]\n    wkb: Py<PyBytes>,\n    #[pyo3(get)]\n    properties: Py<PyDict>,\n}\n\n#[gen_stub_pymethods]\n#[pymethods]\nimpl MltFeature {\n    fn __repr__(&self) -> String {\n        format!(\n            \"MltFeature(id={:?}, geometry_type={:?})\",\n            self.id, self.geometry_type\n        )\n    }\n}\n\nimpl MltFeature {\n    pub(crate) fn new(\n        id: Option<u64>,\n        geometry_type: String,\n        wkb: Py<PyBytes>,\n        properties: Py<PyDict>,\n    ) -> Self {\n        MltFeature {\n            id,\n            geometry_type,\n            wkb,\n            properties,\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-py/src/lib.rs",
    "content": "mod feature;\nmod tile_transform;\n\nuse std::iter::once;\nuse std::ops::Deref;\n\nuse mlt_core::geo_types::{Geometry, LineString, Polygon};\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::{\n    Decoder, GeometryType, Layer, LendingIterator, MltError, MltResult, ParsedLayer01, Parser,\n    PropValueRef,\n};\nuse pyo3::exceptions::PyValueError;\nuse pyo3::prelude::*;\nuse pyo3::types::{PyBytes, PyDict};\nuse pyo3_stub_gen::define_stub_info_gatherer;\nuse pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyfunction, gen_stub_pymethods};\nuse tile_transform::TileTransform;\n\nuse crate::feature::MltFeature;\n\nfn mlt_err(e: MltError) -> PyErr {\n    PyValueError::new_err(format!(\"MLT decode error: {e}\"))\n}\n\n/// A decoded MLT layer containing features.\n#[gen_stub_pyclass]\n#[pyclass]\nstruct MltLayer {\n    #[pyo3(get)]\n    name: String,\n    #[pyo3(get)]\n    extent: u32,\n    #[pyo3(get)]\n    features: Vec<Py<MltFeature>>,\n}\n\n#[gen_stub_pymethods]\n#[pymethods]\nimpl MltLayer {\n    fn __repr__(&self) -> String {\n        format!(\n            \"MltLayer(name={:?}, extent={}, features=<{} features>)\",\n            self.name,\n            self.extent,\n            self.features.len()\n        )\n    }\n}\n\nfn push_coord_raw(buf: &mut Vec<u8>, coord: [i32; 2]) {\n    buf.extend_from_slice(&f64::from(coord[0]).to_le_bytes());\n    buf.extend_from_slice(&f64::from(coord[1]).to_le_bytes());\n}\n\nfn push_coord_xform(buf: &mut Vec<u8>, coord: [i32; 2], xf: TileTransform) {\n    let [x, y] = xf.apply(coord);\n    buf.extend_from_slice(&x.to_le_bytes());\n    buf.extend_from_slice(&y.to_le_bytes());\n}\n\nfn push_coord(buf: &mut Vec<u8>, coord: [i32; 2], xf: Option<TileTransform>) {\n    match xf {\n        Some(xf) => push_coord_xform(buf, coord, xf),\n        None => push_coord_raw(buf, coord),\n    }\n}\n\nfn push_u32(buf: &mut Vec<u8>, v: u32) {\n    buf.extend_from_slice(&v.to_le_bytes());\n}\n\nfn push_rings(\n    buf: &mut Vec<u8>,\n    rings: impl IntoIterator<Item = impl Deref<Target = LineString<i32>>>,\n    xf: Option<TileTransform>,\n) {\n    for ring in rings {\n        push_u32(buf, ring.0.len() as u32);\n        for c in &ring.0 {\n            push_coord(buf, (*c).into(), xf);\n        }\n    }\n}\n\nfn push_linestring(\n    buf: &mut Vec<u8>,\n    line: impl Deref<Target = LineString<i32>>,\n    xf: Option<TileTransform>,\n) {\n    buf.push(0x01);\n    push_u32(buf, 2);\n    push_rings(buf, once(line), xf);\n}\n\nfn push_polygon(buf: &mut Vec<u8>, poly: &Polygon<i32>, xf: Option<TileTransform>) {\n    buf.push(0x01);\n    push_u32(buf, 3);\n    push_u32(buf, (poly.interiors().len() + 1) as u32);\n    push_rings(buf, once(poly.exterior()).chain(poly.interiors()), xf);\n}\n\nfn geom32_to_wkb(geom: &Geometry<i32>, xf: Option<TileTransform>) -> MltResult<Vec<u8>> {\n    let mut buf = Vec::with_capacity(128);\n    match geom {\n        Geometry::<i32>::Point(c) => {\n            buf.push(0x01);\n            push_u32(&mut buf, 1);\n            push_coord(&mut buf, (*c).into(), xf);\n        }\n        Geometry::<i32>::LineString(coords) => push_linestring(&mut buf, coords, xf),\n        Geometry::<i32>::Polygon(poly) => push_polygon(&mut buf, poly, xf),\n        Geometry::<i32>::MultiPoint(coords) => {\n            buf.push(0x01);\n            push_u32(&mut buf, 4);\n            push_u32(&mut buf, coords.0.len() as u32);\n            for c in &coords.0 {\n                buf.push(0x01);\n                push_u32(&mut buf, 1);\n                push_coord(&mut buf, (*c).into(), xf);\n            }\n        }\n        Geometry::<i32>::MultiLineString(lines) => {\n            buf.push(0x01);\n            push_u32(&mut buf, 5);\n            push_u32(&mut buf, lines.0.len() as u32);\n            for line in &lines.0 {\n                push_linestring(&mut buf, line, xf);\n            }\n        }\n        Geometry::<i32>::MultiPolygon(polygons) => {\n            buf.push(0x01);\n            push_u32(&mut buf, 6);\n            push_u32(&mut buf, polygons.0.len() as u32);\n            for polygon in &polygons.0 {\n                push_polygon(&mut buf, polygon, xf);\n            }\n        }\n        _ => return Err(MltError::NotImplemented(\"unsupported geometry type\")),\n    }\n    Ok(buf)\n}\n\nfn prop_value_to_py(py: Python<'_>, v: PropValueRef<'_>) -> Py<PyAny> {\n    match v {\n        PropValueRef::Bool(b) => b.into_pyobject(py).unwrap().to_owned().into_any().unbind(),\n        PropValueRef::I8(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::U8(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::I32(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::U32(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::I64(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::U64(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::F32(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::F64(n) => n.into_pyobject(py).unwrap().into_any().unbind(),\n        PropValueRef::Str(s) => s.into_pyobject(py).unwrap().into_any().unbind(),\n    }\n}\n\nfn build_features(\n    py: Python<'_>,\n    layer: &ParsedLayer01<'_>,\n    xf: Option<TileTransform>,\n) -> PyResult<Vec<Py<MltFeature>>> {\n    let mut features = Vec::new();\n    let mut feat_iter = layer.iter_features();\n    while let Some(feat_result) = feat_iter.next() {\n        let feat = feat_result.map_err(mlt_err)?;\n        let geometry_type = GeometryType::try_from(&feat.geometry)\n            .map(|gt| gt.to_string())\n            .unwrap_or_else(|_| \"Unknown\".to_string());\n        let wkb_bytes = geom32_to_wkb(&feat.geometry, xf).map_err(mlt_err)?;\n        let wkb = PyBytes::new(py, &wkb_bytes).unbind();\n        let prop_dict = PyDict::new(py);\n        for p in feat.iter_properties() {\n            prop_dict.set_item(p.name.to_string(), prop_value_to_py(py, p.value))?;\n        }\n        let feature = MltFeature::new(feat.id, geometry_type, wkb, prop_dict.unbind());\n        features.push(Py::new(py, feature)?);\n    }\n    Ok(features)\n}\n\n/// Decode an MLT binary blob into a list of `MltLayer` objects.\n///\n/// If `z`, `x`, `y` are provided, tile-local coordinates are transformed\n/// to EPSG:3857 (Web Mercator) meters. Without them, raw tile coordinates\n/// are preserved.\n///\n/// `tms`: when True (the default), treat `y` as TMS convention (y=0 at south,\n/// used by OpenMapTiles / MBTiles). Set to False for XYZ / slippy-map tiles\n/// (y=0 at north, e.g. OSM raster tiles).\n#[gen_stub_pyfunction]\n#[pyfunction]\n#[pyo3(signature = (data, z=None, x=None, y=None, tms=true))]\nfn decode_mlt(\n    py: Python<'_>,\n    #[gen_stub(override_type(type_repr = \"bytes\"))] data: &[u8],\n    z: Option<u32>,\n    x: Option<u32>,\n    y: Option<u32>,\n    tms: bool,\n) -> PyResult<Vec<MltLayer>> {\n    let mut dec = Decoder::default();\n    let mut result = Vec::new();\n    for lazy_layer in Parser::default().parse_layers(data).map_err(mlt_err)? {\n        let Layer::Tag01(layer01) = lazy_layer else {\n            return Err(PyValueError::new_err(\n                \"unsupported layer tag (expected 0x01)\",\n            ));\n        };\n        let decoded = layer01.decode_all(&mut dec).map_err(mlt_err)?;\n        let xf = match (z, x, y) {\n            (Some(z), Some(x), Some(y)) => {\n                Some(TileTransform::from_zxy(z, x, y, decoded.extent, tms)?)\n            }\n            _ => None,\n        };\n        result.push(MltLayer {\n            name: decoded.name.to_string(),\n            extent: decoded.extent,\n            features: build_features(py, &decoded, xf)?,\n        });\n    }\n\n    Ok(result)\n}\n\n/// Decode an MLT binary blob and return GeoJSON as a string.\n#[gen_stub_pyfunction]\n#[pyfunction]\nfn decode_mlt_to_geojson(\n    #[gen_stub(override_type(type_repr = \"bytes\"))] data: &[u8],\n) -> PyResult<String> {\n    let mut dec = Decoder::default();\n    let layers = dec\n        .decode_all(Parser::default().parse_layers(data).map_err(mlt_err)?)\n        .map_err(mlt_err)?;\n    let fc = FeatureCollection::from_layers(layers).map_err(mlt_err)?;\n    serde_json::to_string(&fc).map_err(|e| PyValueError::new_err(format!(\"JSON error: {e}\")))\n}\n\n/// Return a list of layer names without fully decoding.\n#[gen_stub_pyfunction]\n#[pyfunction]\nfn list_layers(\n    #[gen_stub(override_type(type_repr = \"bytes\"))] data: &[u8],\n) -> PyResult<Vec<String>> {\n    let layers = Parser::default().parse_layers(data).map_err(mlt_err)?;\n    Ok(layers\n        .iter()\n        .filter_map(|l| l.as_layer01().map(|l| l.name.to_string()))\n        .collect())\n}\n\n#[pymodule]\nfn maplibre_tiles(m: &Bound<'_, PyModule>) -> PyResult<()> {\n    m.add_function(wrap_pyfunction!(decode_mlt, m)?)?;\n    m.add_function(wrap_pyfunction!(decode_mlt_to_geojson, m)?)?;\n    m.add_function(wrap_pyfunction!(list_layers, m)?)?;\n    m.add_class::<MltLayer>()?;\n    m.add_class::<MltFeature>()?;\n    Ok(())\n}\n\ndefine_stub_info_gatherer!(stub_info);\n\n#[cfg(test)]\nmod tests {\n    use std::f64::consts::PI;\n    use std::fs;\n\n    use mlt_core::{Decoder, GeometryValues};\n\n    use super::*;\n\n    fn geom_to_wkb(\n        geom: &GeometryValues,\n        index: usize,\n        xf: Option<TileTransform>,\n    ) -> MltResult<Vec<u8>> {\n        geom32_to_wkb(&geom.to_geojson(index)?, xf)\n    }\n\n    #[test]\n    fn tile_transform_rejects_zoom_above_30() {\n        let result = TileTransform::from_zxy(31, 0, 0, 4096, false);\n        assert!(result.is_err(), \"z=31 should be rejected\");\n\n        let result = TileTransform::from_zxy(30, 0, 0, 4096, false);\n        assert!(result.is_ok(), \"z=30 should be accepted\");\n\n        let result = TileTransform::from_zxy(0, 0, 0, 4096, false);\n        assert!(result.is_ok(), \"z=0 should be accepted\");\n    }\n\n    #[test]\n    fn tile_transform_zoom_zero_covers_world() {\n        let xf = TileTransform::from_zxy(0, 0, 0, 4096, false).unwrap();\n\n        let circumference = 2.0 * PI * 6_378_137.0;\n        let half = circumference / 2.0;\n\n        assert!(\n            (xf.x_origin + half).abs() < 1.0,\n            \"x_origin at z=0 should be -half_circumference\"\n        );\n        assert!(\n            (xf.y_origin - half).abs() < 1.0,\n            \"y_origin at z=0 should be +half_circumference\"\n        );\n\n        let tile_scale = circumference / 4096.0;\n        assert!(\n            (xf.x_scale - tile_scale).abs() < 1e-6,\n            \"x_scale should equal circumference / extent\"\n        );\n        assert!(\n            (xf.y_scale + tile_scale).abs() < 1e-6,\n            \"y_scale should be negative (flipped)\"\n        );\n    }\n\n    #[test]\n    fn tile_transform_apply_maps_origin_and_extent() {\n        let xf = TileTransform::from_zxy(0, 0, 0, 4096, false).unwrap();\n\n        let origin = xf.apply([0, 0]);\n        assert!(\n            (origin[0] - xf.x_origin).abs() < 1e-6,\n            \"apply([0,0]).x should equal x_origin\"\n        );\n        assert!(\n            (origin[1] - xf.y_origin).abs() < 1e-6,\n            \"apply([0,0]).y should equal y_origin\"\n        );\n\n        let far_corner = xf.apply([4096, 4096]);\n        let circumference = 2.0 * PI * 6_378_137.0;\n        let half = circumference / 2.0;\n        assert!(\n            (far_corner[0] - half).abs() < 1.0,\n            \"apply([4096,4096]).x should reach +half\"\n        );\n        assert!(\n            (far_corner[1] + half).abs() < 1.0,\n            \"apply([4096,4096]).y should reach -half\"\n        );\n    }\n\n    #[test]\n    fn tile_transform_tms_vs_xyz() {\n        let xyz = TileTransform::from_zxy(1, 0, 0, 4096, false).unwrap();\n        let tms = TileTransform::from_zxy(1, 0, 1, 4096, true).unwrap();\n\n        assert!(\n            (xyz.x_origin - tms.x_origin).abs() < 1e-6,\n            \"same tile via TMS and XYZ should produce same x_origin\"\n        );\n        assert!(\n            (xyz.y_origin - tms.y_origin).abs() < 1e-6,\n            \"same tile via TMS and XYZ should produce same y_origin\"\n        );\n    }\n\n    #[test]\n    fn fixture_parse_and_feature_collection() {\n        let fixture_path = \"../../test/synthetic/0x01/point.mlt\";\n        let data = fs::read(fixture_path)\n            .unwrap_or_else(|e| panic!(\"failed to read fixture {fixture_path}: {e}\"));\n\n        let layers = Parser::default()\n            .parse_layers(&data)\n            .expect(\"parse_layers should succeed\");\n        let mut dec = Decoder::default();\n        let decoded = dec.decode_all(layers).expect(\"decode_all should succeed\");\n\n        assert!(!decoded.is_empty(), \"should parse at least one layer\");\n        let l = decoded[0].as_layer01().expect(\"first layer should be v0.1\");\n        assert!(!l.name.is_empty(), \"layer name should be non-empty\");\n\n        let fc = FeatureCollection::from_layers(decoded).expect(\"FeatureCollection should succeed\");\n        assert!(\n            !fc.features.is_empty(),\n            \"feature collection should have features\"\n        );\n    }\n\n    #[test]\n    fn fixture_geom_to_wkb_produces_valid_output() {\n        let fixture_path = \"../../test/synthetic/0x01/poly.mlt\";\n        let data = fs::read(fixture_path)\n            .unwrap_or_else(|e| panic!(\"failed to read fixture {fixture_path}: {e}\"));\n\n        let layers = Parser::default()\n            .parse_layers(&data)\n            .expect(\"parse_layers should succeed\");\n        let mut dec = Decoder::default();\n        let decoded = dec.decode_all(layers).expect(\"decode_all should succeed\");\n\n        let l = decoded[0].as_layer01().expect(\"first layer should be v0.1\");\n        let geom = l.geometry_values();\n\n        let wkb = geom_to_wkb(geom, 0, None).expect(\"geom_to_wkb should succeed\");\n        assert!(\n            wkb.len() >= 5,\n            \"WKB must be at least 5 bytes (byte order + type)\"\n        );\n        assert_eq!(wkb[0], 0x01, \"WKB byte order should be little-endian\");\n        let wkb_type = u32::from_le_bytes([wkb[1], wkb[2], wkb[3], wkb[4]]);\n        assert_eq!(\n            wkb_type, 3,\n            \"polygon fixture should produce WKB type 3 (Polygon)\"\n        );\n    }\n\n    #[test]\n    fn fixture_geom_to_wkb_with_transform() {\n        let fixture_path = \"../../test/synthetic/0x01/point.mlt\";\n        let data = fs::read(fixture_path)\n            .unwrap_or_else(|e| panic!(\"failed to read fixture {fixture_path}: {e}\"));\n\n        let layers = Parser::default()\n            .parse_layers(&data)\n            .expect(\"parse_layers should succeed\");\n        let mut dec = Decoder::default();\n        let decoded = dec.decode_all(layers).expect(\"decode_all should succeed\");\n\n        let l = decoded[0].as_layer01().expect(\"first layer should be v0.1\");\n        let geom = l.geometry_values();\n\n        let xf = TileTransform::from_zxy(0, 0, 0, l.extent, false).unwrap();\n\n        let wkb_raw = geom_to_wkb(geom, 0, None).expect(\"raw wkb should succeed\");\n        let wkb_xf = geom_to_wkb(geom, 0, Some(xf)).expect(\"transformed wkb should succeed\");\n\n        assert_eq!(\n            wkb_raw.len(),\n            wkb_xf.len(),\n            \"raw and transformed WKB should have the same length\"\n        );\n        assert_ne!(\n            wkb_raw, wkb_xf,\n            \"transformed WKB should differ from raw (unless coordinates are trivially 0)\"\n        );\n    }\n\n    #[test]\n    fn fixture_line_produces_wkb_linestring() {\n        let fixture_path = \"../../test/synthetic/0x01/line.mlt\";\n        let data = fs::read(fixture_path)\n            .unwrap_or_else(|e| panic!(\"failed to read fixture {fixture_path}: {e}\"));\n\n        let layers = Parser::default()\n            .parse_layers(&data)\n            .expect(\"parse_layers should succeed\");\n        let mut dec = Decoder::default();\n        let decoded = dec.decode_all(layers).expect(\"decode_all should succeed\");\n\n        let l = decoded[0].as_layer01().expect(\"first layer should be v0.1\");\n        let geom = l.geometry_values();\n\n        let wkb = geom_to_wkb(geom, 0, None).expect(\"geom_to_wkb should succeed\");\n        assert!(wkb.len() >= 5);\n        let wkb_type = u32::from_le_bytes([wkb[1], wkb[2], wkb[3], wkb[4]]);\n        assert_eq!(\n            wkb_type, 2,\n            \"line fixture should produce WKB type 2 (LineString)\"\n        );\n    }\n}\n"
  },
  {
    "path": "rust/mlt-py/src/tile_transform.rs",
    "content": "use std::f64::consts::PI;\n\nuse pyo3::PyErr;\nuse pyo3::exceptions::PyValueError;\n\n/// Affine transform from tile-local coords to EPSG:3857 meters.\n#[derive(Clone, Copy)]\npub struct TileTransform {\n    pub x_origin: f64,\n    pub y_origin: f64,\n    pub x_scale: f64,\n    pub y_scale: f64,\n}\n\nimpl TileTransform {\n    /// Build a transform from tile z/x/y coordinates.\n    ///\n    /// `tms`: if true, y uses TMS convention (y=0 at south, used by OpenMapTiles\n    /// and MBTiles). If false, y uses XYZ / slippy-map convention (y=0 at north,\n    /// used by OSM tile servers).\n    pub fn from_zxy(z: u32, x: u32, y: u32, extent: u32, tms: bool) -> Result<Self, PyErr> {\n        if z > 30 {\n            return Err(PyValueError::new_err(format!(\n                \"zoom level {z} exceeds maximum of 30\"\n            )));\n        }\n\n        let n = f64::from(1_u32 << z);\n        let circumference = 2.0 * PI * 6_378_137.0;\n        let tile_size = circumference / n;\n        let half = circumference / 2.0;\n\n        // Convert TMS y to XYZ y if needed (y_xyz = 2^z - 1 - y_tms)\n        let y_xyz = if tms {\n            (1_u32 << z).saturating_sub(1).saturating_sub(y)\n        } else {\n            y\n        };\n\n        // In XYZ convention: y=0 is the north edge of the map.\n        // The tile's north (top) edge in EPSG:3857 meters:\n        let x_origin = f64::from(x) * tile_size - half;\n        let y_origin = half - f64::from(y_xyz) * tile_size;\n\n        let scale = tile_size / f64::from(extent);\n\n        Ok(TileTransform {\n            x_origin,\n            y_origin,\n            x_scale: scale,\n            y_scale: -scale, // tile pixel-y grows downward, EPSG:3857 y grows upward\n        })\n    }\n\n    pub fn apply(self, coord: [i32; 2]) -> [f64; 2] {\n        [\n            self.x_origin + f64::from(coord[0]) * self.x_scale,\n            self.y_origin + f64::from(coord[1]) * self.y_scale,\n        ]\n    }\n}\n"
  },
  {
    "path": "rust/mlt-synthetics/Cargo.toml",
    "content": "[package]\nname = \"mlt-synthetics\"\nversion = \"0.1.0\"\nauthors.workspace = true\ncategories.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\npublish = false\n\n[dependencies]\nclap.workspace = true\nmlt-core = { workspace = true, features = [\"__private\"] }\nserde_json.workspace = true\nthiserror.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "rust/mlt-synthetics/src/layer.rs",
    "content": "use std::collections::{HashMap, HashSet};\nuse std::fs::{File, OpenOptions};\nuse std::io;\nuse std::path::Path;\n\nuse mlt_core::GeometryValues;\nuse mlt_core::encoder::{\n    Codecs, ColumnKind, Encoder, EncoderConfig, ExplicitEncoder, IntEncoder, Presence, StagedId,\n    StagedLayer, StagedProperty, StagedSharedDict, StrEncoding, StreamCtx, VertexBufferType,\n};\nuse mlt_core::geo_types::{Coord, Geometry};\nuse mlt_core::wire::{LengthType, OffsetType, StreamType};\n\nuse crate::writer::{SynthErr, SynthResult, SynthWriter};\n\n/// Create a layer with all geometry encoders set to `VarInt`.\npub fn geo_varint() -> Layer {\n    Layer::new(IntEncoder::varint())\n}\n\n/// Create a layer with geometry encoders set to `VarInt` and RLE for the meta stream.\npub fn geo_varint_with_rle() -> Layer {\n    Layer::new(IntEncoder::varint()).meta(IntEncoder::rle_varint())\n}\n\n/// Create a layer with all geometry encoders set to `FastPFOR`.\npub fn geo_fastpfor() -> Layer {\n    Layer::new(IntEncoder::fastpfor())\n}\n\n/// Per-property encoding specification.\n#[derive(Clone)]\nenum PropConfig {\n    /// Int/Bool/Float: `enc` is used for integer streams; Bool/Float auto-detect from type.\n    Scalar(IntEncoder),\n    /// String FSST encoding.\n    StrFsst {\n        sym_lengths: IntEncoder,\n        dict_lengths: IntEncoder,\n    },\n    /// String FSST+Dictionary encoding.\n    StrFsstDict {\n        sym_lengths: IntEncoder,\n        dict_lengths: IntEncoder,\n        offsets: IntEncoder,\n    },\n    /// String Dictionary (plain dict) encoding.\n    StrDict {\n        string_lengths: IntEncoder,\n        offsets: IntEncoder,\n    },\n    /// Shared dictionary: `StrEncoding` for the corpus, per-suffix `IntEncoder` for offsets.\n    SharedDict {\n        dict_encoding: StrEncoding,\n        item_encs: Vec<(String, IntEncoder)>,\n    },\n}\n\nimpl PropConfig {\n    fn str_encoding(&self) -> StrEncoding {\n        match self {\n            Self::Scalar(_) => StrEncoding::Plain,\n            Self::StrFsst { .. } => StrEncoding::Fsst,\n            Self::StrFsstDict { .. } => StrEncoding::FsstDict,\n            Self::StrDict { .. } => StrEncoding::Dict,\n            Self::SharedDict { dict_encoding, .. } => *dict_encoding,\n        }\n    }\n\n    /// Resolve the integer encoder for a property stream using wire `StreamType`.\n    fn int_enc_for_stream_ctx(&self, ctx: &StreamCtx<'_>) -> IntEncoder {\n        use LengthType as LT;\n        use OffsetType as OT;\n        use StreamType as ST;\n        match self {\n            Self::Scalar(e) => *e,\n            Self::StrFsst {\n                sym_lengths,\n                dict_lengths,\n            } => match ctx.stream_type {\n                ST::Length(LT::Symbol) => *sym_lengths,\n                _ => *dict_lengths,\n            },\n            Self::StrFsstDict {\n                sym_lengths,\n                dict_lengths,\n                offsets,\n            } => match ctx.stream_type {\n                ST::Length(LT::Symbol) => *sym_lengths,\n                ST::Offset(OT::String) => *offsets,\n                _ => *dict_lengths,\n            },\n            Self::StrDict {\n                string_lengths,\n                offsets,\n            } => match ctx.stream_type {\n                ST::Offset(OT::String) => *offsets,\n                _ => *string_lengths,\n            },\n            Self::SharedDict { item_encs, .. } => {\n                // sub is the item suffix\n                item_encs\n                    .iter()\n                    .find(|(k, _)| k == ctx.subname)\n                    .map(|(_, e)| *e)\n                    .or_else(|| item_encs.first().map(|(_, e)| *e))\n                    .unwrap_or_else(IntEncoder::varint)\n            }\n        }\n    }\n}\n\n/// Layer builder for synthetic tile generation.\n#[derive(Clone)]\npub struct Layer {\n    /// Default encoder for all geometry streams.\n    default_geo_enc: IntEncoder,\n    /// Per-stream overrides; key is the stream name (e.g. `\"meta\"`, `\"rings\"`).\n    geo_stream_overrides: HashMap<&'static str, IntEncoder>,\n    vertex_buffer_type: VertexBufferType,\n    tessellate: bool,\n    /// Geometry stream names that must be written even when their data is empty.\n    /// See [`ExplicitEncoder::force_stream`] for details.\n    force_empty_streams: HashSet<&'static str>,\n    geometry_items: Vec<Geometry<i32>>,\n    props: Vec<(StagedProperty, PropConfig)>,\n    extent: Option<u32>,\n    ids: Option<(StagedId, IntEncoder)>,\n}\n\nimpl Layer {\n    fn new(default_enc: IntEncoder) -> Self {\n        Self {\n            default_geo_enc: default_enc,\n            geo_stream_overrides: HashMap::new(),\n            vertex_buffer_type: VertexBufferType::Vec2,\n            tessellate: false,\n            force_empty_streams: HashSet::new(),\n            geometry_items: vec![],\n            props: vec![],\n            extent: None,\n            ids: None,\n        }\n    }\n\n    #[must_use]\n    pub fn meta(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"meta\", e);\n        self\n    }\n    #[must_use]\n    pub fn rings(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"rings\", e);\n        self\n    }\n    #[must_use]\n    pub fn rings2(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"rings2\", e);\n        self\n    }\n    #[must_use]\n    pub fn no_rings(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"no_rings\", e);\n        self\n    }\n    #[must_use]\n    pub fn parts_ring(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"parts_ring\", e);\n        self\n    }\n    #[must_use]\n    pub fn vertex_offsets(mut self, e: IntEncoder) -> Self {\n        self.geo_stream_overrides.insert(\"vertex_offsets\", e);\n        self\n    }\n    #[must_use]\n    pub fn vertex_buffer_type(mut self, v: VertexBufferType) -> Self {\n        self.vertex_buffer_type = v;\n        self\n    }\n    #[must_use]\n    pub fn tessellate(mut self) -> Self {\n        self.tessellate = true;\n        self\n    }\n\n    /// Force a geometry stream to be written even when its data is empty.\n    ///\n    /// `name` is the geometry stream name as used internally by the encoder\n    /// (e.g. `\"triangles_indexes\"`, `\"geometries\"`, `\"rings\"`, …).\n    ///\n    /// Useful for producing byte-for-byte output that matches Java's encoder when a\n    /// normally-empty stream must still appear in the wire format.\n    #[must_use]\n    pub fn force_empty_stream(mut self, name: &'static str) -> Self {\n        self.force_empty_streams.insert(name);\n        self\n    }\n\n    #[must_use]\n    pub fn geo(mut self, geometry: impl Into<Geometry<i32>>) -> Self {\n        self.geometry_items.push(geometry.into());\n        self\n    }\n\n    #[must_use]\n    pub fn geos<T: Into<Geometry<i32>>, I: IntoIterator<Item = T>>(\n        mut self,\n        geometries: I,\n    ) -> Self {\n        for g in geometries {\n            self = self.geo(g.into());\n        }\n        self\n    }\n\n    /// Add a bool, integer, or float property.\n    ///\n    /// `enc` is used for integer stream encoding; Bool and Float columns ignore it.\n    #[must_use]\n    pub fn add_prop(mut self, enc: IntEncoder, prop: StagedProperty) -> Self {\n        self.props.push((prop, PropConfig::Scalar(enc)));\n        self\n    }\n\n    /// Add an FSST-compressed string property.\n    #[must_use]\n    pub fn add_prop_str_fsst(\n        mut self,\n        sym_lengths: IntEncoder,\n        dict_lengths: IntEncoder,\n        prop: StagedProperty,\n    ) -> Self {\n        self.props.push((\n            prop,\n            PropConfig::StrFsst {\n                sym_lengths,\n                dict_lengths,\n            },\n        ));\n        self\n    }\n\n    /// Add a Dictionary (plain dict) string property.\n    #[must_use]\n    pub fn add_prop_str_dict(\n        mut self,\n        string_lengths: IntEncoder,\n        offsets: IntEncoder,\n        prop: StagedProperty,\n    ) -> Self {\n        self.props.push((\n            prop,\n            PropConfig::StrDict {\n                string_lengths,\n                offsets,\n            },\n        ));\n        self\n    }\n\n    /// Add an FSST+Dictionary string property.\n    #[must_use]\n    pub fn add_prop_str_fsst_dict(\n        mut self,\n        sym_lengths: IntEncoder,\n        dict_lengths: IntEncoder,\n        offsets: IntEncoder,\n        prop: StagedProperty,\n    ) -> Self {\n        self.props.push((\n            prop,\n            PropConfig::StrFsstDict {\n                sym_lengths,\n                dict_lengths,\n                offsets,\n            },\n        ));\n        self\n    }\n\n    /// Add a shared dictionary column.\n    #[must_use]\n    pub fn add_shared_dict(mut self, shared_dict: SharedDict) -> Self {\n        let dict_encoding = shared_dict.dict_encoding;\n        let item_encs: Vec<(String, IntEncoder)> = shared_dict\n            .items\n            .iter()\n            .map(|(suffix, enc, _, _)| (suffix.clone(), *enc))\n            .collect();\n        let dict = StagedSharedDict::new(\n            shared_dict.name,\n            shared_dict\n                .items\n                .into_iter()\n                .map(|(suffix, _, vals, is_optional)| {\n                    let presence = if is_optional {\n                        Presence::Mixed\n                    } else {\n                        Presence::AllPresent\n                    };\n                    (suffix, vals, presence)\n                }),\n        )\n        .expect(\"shared dict builder should be valid\");\n        self.props.push((\n            StagedProperty::SharedDict(dict),\n            PropConfig::SharedDict {\n                dict_encoding,\n                item_encs,\n            },\n        ));\n        self\n    }\n\n    /// Encode and then either verify against the reference dir (non-rust files) or write to the\n    /// output dir (`-rust`-suffixed files). Delegates to [`SynthWriter::write`].\n    ///\n    /// When `force_empty_streams` is non-empty, also emits a `_ns` (\"no forced stream\")\n    /// sibling — but only when removing the forced-empty-stream flag **actually changes the\n    /// encoded output**.  For some geometry configurations (e.g. Multi* types where the\n    /// GEOMETRIES stream is already non-empty) the flag is a no-op; emitting the sibling in\n    /// those cases would produce duplicate MLT files and fail the uniqueness check.\n    pub fn write(self, w: &mut SynthWriter, name: impl AsRef<str>) {\n        if !self.force_empty_streams.is_empty() {\n            let forced_bytes = self.clone().encode_to_bytes().ok();\n            let mut ns_layer = self.clone();\n            ns_layer.force_empty_streams.clear();\n            let ns_bytes = ns_layer.clone().encode_to_bytes().ok();\n            if forced_bytes != ns_bytes {\n                let name = if let Some(prefix) = name.as_ref().strip_suffix(\"-rust\") {\n                    format!(\"{prefix}_ns-rust\")\n                } else {\n                    format!(\"{}_ns\", name.as_ref())\n                };\n                w.write(ns_layer, name);\n            }\n        }\n        w.write(self, name);\n    }\n\n    #[must_use]\n    pub fn extent(mut self, extent: u32) -> Self {\n        self.extent = Some(extent);\n        self\n    }\n\n    /// Set feature IDs with explicit encoding.\n    #[must_use]\n    pub fn ids(mut self, ids: StagedId, int_enc: IntEncoder) -> Self {\n        self.ids = Some((ids, int_enc));\n        self\n    }\n\n    pub fn open_new(path: &Path) -> io::Result<File> {\n        OpenOptions::new().write(true).create_new(true).open(path)\n    }\n\n    pub fn encode_to_bytes(self) -> SynthResult<Vec<u8>> {\n        let Self {\n            default_geo_enc,\n            geo_stream_overrides,\n            vertex_buffer_type,\n            tessellate,\n            force_empty_streams,\n            geometry_items,\n            props,\n            extent,\n            ids,\n        } = self;\n\n        let enc_cfg = EncoderConfig {\n            tessellate,\n            ..EncoderConfig::default()\n        };\n\n        let mut geometry = if enc_cfg.tessellate {\n            GeometryValues::new_tessellated()\n        } else {\n            GeometryValues::default()\n        };\n        for geom in &geometry_items {\n            geometry.push_geom(geom);\n        }\n\n        let (id, id_int_enc) = match ids {\n            Some((ids, int_enc)) => (ids, Some(int_enc)),\n            None => (StagedId::None, None),\n        };\n\n        // Build name→PropConfig map for the ExplicitEncoder callbacks.\n        let prop_map: HashMap<String, PropConfig> = props\n            .iter()\n            .map(|(p, c)| (p.name().to_string(), c.clone()))\n            .collect();\n\n        let cfg = ExplicitEncoder {\n            vertex_buffer_type,\n            force_stream: Box::new(move |ctx: &StreamCtx<'_>| {\n                ctx.kind == ColumnKind::Geometry && force_empty_streams.contains(ctx.name)\n            }),\n            get_int_encoder: {\n                let prop_map = prop_map.clone();\n                Box::new(move |ctx: &StreamCtx<'_>| match ctx.kind {\n                    ColumnKind::Id => id_int_enc.unwrap_or_else(IntEncoder::varint),\n                    ColumnKind::Geometry => geo_stream_overrides\n                        .get(ctx.name)\n                        .copied()\n                        .unwrap_or(default_geo_enc),\n                    ColumnKind::Property => prop_map\n                        .get(ctx.name)\n                        .map_or_else(IntEncoder::varint, |c| c.int_enc_for_stream_ctx(ctx)),\n                })\n            },\n            get_str_encoding: {\n                Box::new(move |name: &str| {\n                    prop_map\n                        .get(name)\n                        .map_or(StrEncoding::Plain, PropConfig::str_encoding)\n                })\n            },\n        };\n\n        let mut codecs = Codecs::default();\n        StagedLayer {\n            name: \"layer1\".to_string(),\n            extent: extent.unwrap_or(80),\n            id,\n            geometry,\n            properties: props.into_iter().map(|(p, _)| p).collect(),\n        }\n        .encode_into(Encoder::with_explicit(enc_cfg, cfg), &mut codecs)?\n        .into_layer_bytes()\n        .map_err(SynthErr::Mlt)\n    }\n}\n\n/// Builder for a shared dictionary struct column with multiple string sub-properties.\npub struct SharedDict {\n    name: String,\n    dict_encoding: StrEncoding,\n    /// `(suffix, encoder, values, is_optional)`\n    items: Vec<(String, IntEncoder, Vec<Option<String>>, bool)>,\n}\n\nimpl SharedDict {\n    /// Create a new shared dictionary builder.\n    ///\n    /// # Arguments\n    /// * `name` - The name for the property (e.g., `\"name:\"` for `\"name:de\"`, `\"name:en\"`).\n    /// * `dict_encoding` - The string encoding for the shared dictionary corpus (plain or FSST).\n    #[must_use]\n    pub fn new(name: impl Into<String>, dict_encoding: StrEncoding) -> Self {\n        Self {\n            name: name.into(),\n            dict_encoding,\n            items: vec![],\n        }\n    }\n\n    /// Add a non-optional child column (no presence stream will be written).\n    #[must_use]\n    pub fn col<S: Into<String>>(\n        mut self,\n        suffix: impl Into<String>,\n        offsets: IntEncoder,\n        values: impl IntoIterator<Item = S>,\n    ) -> Self {\n        self.items.push((\n            suffix.into(),\n            offsets,\n            values.into_iter().map(|v| Some(v.into())).collect(),\n            false,\n        ));\n        self\n    }\n\n    /// Add an optional child column (a presence stream is always written).\n    #[must_use]\n    pub fn opt(\n        mut self,\n        suffix: impl Into<String>,\n        offsets: IntEncoder,\n        values: impl IntoIterator<Item = Option<String>>,\n    ) -> Self {\n        self.items\n            .push((suffix.into(), offsets, values.into_iter().collect(), true));\n        self\n    }\n}\n\n/// Morton (Z-order) curve: de-interleave index bits into x/y (even/odd bits).\n/// Produces a 4×4 complete Morton block (16 points, scale 8).\npub fn morton_curve() -> Vec<Coord<i32>> {\n    let num_points = 16usize;\n    let scale = 8_i32;\n    let morton_bits = 4u32;\n    let mut curve = Vec::with_capacity(num_points);\n    for i in 0..num_points {\n        let i = i32::try_from(i).unwrap();\n        let mut x = 0_i32;\n        let mut y = 0_i32;\n        for b in 0..morton_bits {\n            x |= ((i >> (2 * b)) & 1) << b;\n            y |= ((i >> (2 * b + 1)) & 1) << b;\n        }\n        curve.push(crate::c(x * scale, y * scale));\n    }\n    curve\n}\n"
  },
  {
    "path": "rust/mlt-synthetics/src/main.rs",
    "content": "//! Rust synthetic MLT file generator.\n//!\n//! Verifies non-rust synthetics in-memory against the reference `0x01/` dir.\n//!\n//! * If the test exists in `0x01/` dir, validates that Rust-generated one is identical\n//! * If the test does not match the one in `0x01/` dir, it gets written to `0x01-rust/` dir.\n//! * Tests with `_fsst` in their name are expected to produce different-but-compatible output,\n//!   and their output is placed into `0x01-rust/` dir.\n//! * Tests with the `-rust` suffix are also written to `0x01-rust/` dir, except without the suffix.\n//! * All tests written to `0x01-rust/` are also validated against the .json file with the same name in `0x01/`\n//!\n//! ### Common filename abbreviations\n//! * `np` - no presence stream, i.e. values exist for each feature in a column\n//! * `fpf` - uses `FastPFor` compression\n//! * `tes` - includes tessellation triangles stream\n//! * `ns` - unlike Java encoder, empty streams are not forced to be created\n\nmod layer;\nmod writer;\n\nuse std::fmt::Write as _;\nuse std::path::PathBuf;\nuse std::sync::LazyLock;\n\nuse clap::Parser;\nuse mlt_core::encoder::{\n    IntEncoder as E, LogicalEncoder as L, StagedId as Id, StagedProperty as P, StrEncoding,\n    VertexBufferType,\n};\nuse mlt_core::geo_types::{\n    Coord, Geometry, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, coord,\n    line_string as line, wkt,\n};\n\nuse crate::layer::{\n    Layer, SharedDict, geo_fastpfor, geo_varint, geo_varint_with_rle, morton_curve,\n};\nuse crate::writer::SynthWriter;\n\n#[derive(Parser)]\n#[command(about = \"Verify Rust-generated synthetic MLTs against the Java reference\")]\nstruct Args {\n    /// Print each verified or written file\n    #[arg(long)]\n    verbose: bool,\n\n    /// Directory with the reference synthetic MLT files to verify against (must exist)\n    #[arg(long, default_value = \"../test/synthetic/0x01/\")]\n    synthetics: PathBuf,\n\n    /// Directory to use for Rust-specific synthetic MLT files (will be created if it doesn't exist)\n    #[arg(long, default_value = \"../test/synthetic/0x01-rust/\")]\n    synthetics_rust: PathBuf,\n}\n\nconst C0: Coord<i32> = coord! { x: 13, y: 42 };\n// triangle 1, clockwise winding, X ends in 1, Y ends in 2\nconst C1: Coord<i32> = coord! { x: 11, y: 52 };\nconst C2: Coord<i32> = coord! { x: 71, y: 72 };\nconst C3: Coord<i32> = coord! { x: 61, y: 22 };\n// hole in triangle 1 with counter-clockwise winding\nconst H1: Coord<i32> = coord! { x: 65, y: 66 };\nconst H2: Coord<i32> = coord! { x: 35, y: 56 };\nconst H3: Coord<i32> = coord! { x: 55, y: 36 };\n\nconst P0: Point<i32> = Point(C0);\nconst P1: Point<i32> = Point(C1);\nconst P2: Point<i32> = Point(C2);\nconst P3: Point<i32> = Point(C3);\n// holes as points with same coordinates as the hole vertices\nconst PH1: Point<i32> = Point(H1);\nconst PH2: Point<i32> = Point(H2);\nconst PH3: Point<i32> = Point(H3);\n\nconst fn c(x: i32, y: i32) -> Coord<i32> {\n    coord! { x: x, y: y }\n}\n\nfn p0() -> Layer {\n    geo_varint().geo(P0)\n}\n\nstatic MIX_TYPES: LazyLock<[(&'static str, Geometry<i32>); 7]> = LazyLock::new(|| {\n    [\n        (\"pt\", wkt!(POINT(38 29)).into()),\n        (\"line\", wkt!(LINESTRING(5 38, 12 45, 9 70)).into()),\n        (\"poly\", wkt!(POLYGON((55 5, 58 28, 75 22, 55 5))).into()),\n        (\n            \"polyh\",\n            wkt!(POLYGON((52 35, 14 55, 60 72, 52 35),(32 50, 36 60, 24 54, 32 50))).into(),\n        ),\n        (\"mpt\", wkt!(MULTIPOINT(6 25, 21 41, 23 69)).into()),\n        (\n            \"mline\",\n            wkt!(MULTILINESTRING((24 10, 42 18),(30 36, 48 52, 35 62))).into(),\n        ),\n        (\n            \"mpoly\",\n            wkt!(MULTIPOLYGON(((7 20, 21 31, 26 9, 7 20),(15 20, 20 15, 18 25, 15 20)),((69 57, 71 66, 73 64, 69 57)))).into(),\n        ),\n    ]\n});\n\nfn main() {\n    let mut writer = SynthWriter::new(Args::parse());\n\n    generate_geometry(&mut writer);\n    generate_mixed(&mut writer);\n    generate_extent(&mut writer);\n    generate_ids(&mut writer);\n    generate_properties(&mut writer);\n\n    writer.report_ungenerated();\n\n    if writer.failures > 0 {\n        eprintln!(\"{} synthetics failed\", writer.failures);\n        std::process::exit(1);\n    }\n}\n\n// Geometry builder functions matching Java definitions\nfn line1() -> LineString<i32> {\n    wkt!(LINESTRING(11 52, 71 72, 61 22))\n}\nfn line2() -> LineString<i32> {\n    wkt!(LINESTRING(23 34, 73 4, 13 24))\n}\nfn poly1() -> Polygon<i32> {\n    wkt!(POLYGON((11 52, 71 72, 61 22, 11 52)))\n}\nfn poly2() -> Polygon<i32> {\n    wkt!(POLYGON((23 34, 73 4, 13 24, 23 34)))\n}\nfn poly1h() -> Polygon<i32> {\n    wkt!(POLYGON((11 52, 71 72, 61 22, 11 52),(65 66, 35 56, 55 36, 65 66)))\n}\nfn poly_collinear() -> Polygon<i32> {\n    wkt!(POLYGON((0 0, 10 0, 20 0, 0 0)))\n}\nfn poly_self_intersect() -> Polygon<i32> {\n    wkt!(POLYGON((0 0, 10 10, 0 10, 10 0, 0 0)))\n}\nfn poly_hole_touching() -> Polygon<i32> {\n    wkt!(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0),(0 0, 2 2, 5 2, 0 0)))\n}\n\nfn generate_geometry(w: &mut SynthWriter) {\n    p0().write(w, \"point\");\n    geo_varint().geo(line1()).write(w, \"line\");\n\n    geo_varint()\n        .geo(LineString::new(morton_curve()))\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .write(w, \"line_morton_curve_morton\");\n    geo_varint()\n        .geo(LineString::new(morton_curve()))\n        .vertex_buffer_type(VertexBufferType::Vec2)\n        .vertex_offsets(E::delta_rle_varint())\n        .write(w, \"line_morton_curve_no_morton\");\n    geo_varint()\n        .geo(LineString::new(vec![c(6_i32, 6), c(6, 6)]))\n        .write(w, \"line_zero_length\");\n\n    geo_varint().geo(poly1()).write(w, \"poly\");\n    geo_fastpfor().geo(poly1()).write(w, \"poly_fpf\");\n    geo_varint()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly1())\n        .write(w, \"poly_tes\");\n    geo_fastpfor()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly1())\n        .write(w, \"poly_fpf_tes\");\n\n    geo_varint()\n        .geo(poly_collinear())\n        .write(w, \"poly_collinear\");\n    geo_fastpfor()\n        .geo(poly_collinear())\n        .write(w, \"poly_collinear_fpf\");\n    geo_varint()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .force_empty_stream(\"triangles_indexes\")\n        .geo(poly_collinear())\n        .write(w, \"poly_collinear_tes\");\n    geo_fastpfor()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .force_empty_stream(\"triangles_indexes\")\n        .geo(poly_collinear())\n        .write(w, \"poly_collinear_fpf_tes\");\n\n    geo_varint()\n        .geo(poly_self_intersect())\n        .write(w, \"poly_self_intersect\");\n    geo_fastpfor()\n        .geo(poly_self_intersect())\n        .write(w, \"poly_self_intersect_fpf\");\n    geo_varint()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly_self_intersect())\n        .write(w, \"poly_self_intersect_tes\");\n    geo_fastpfor()\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly_self_intersect())\n        .write(w, \"poly_self_intersect_fpf_tes\");\n\n    geo_varint()\n        .parts_ring(E::rle_varint())\n        .geo(poly1h())\n        .write(w, \"poly_hole\");\n    geo_fastpfor()\n        .parts_ring(E::rle_fastpfor())\n        .geo(poly1h())\n        .write(w, \"poly_hole_fpf\");\n    geo_varint()\n        .parts_ring(E::rle_varint())\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly1h())\n        .write(w, \"poly_hole_tes\");\n    geo_fastpfor()\n        .parts_ring(E::rle_fastpfor())\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly1h())\n        .write(w, \"poly_hole_fpf_tes\");\n\n    geo_varint()\n        .parts_ring(E::varint())\n        .geo(poly_hole_touching())\n        .write(w, \"poly_hole_touching\");\n    geo_fastpfor()\n        .parts_ring(E::fastpfor())\n        .geo(poly_hole_touching())\n        .write(w, \"poly_hole_touching_fpf\");\n    geo_varint()\n        .parts_ring(E::varint())\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly_hole_touching())\n        .write(w, \"poly_hole_touching_tes\");\n    geo_fastpfor()\n        .parts_ring(E::fastpfor())\n        .tessellate()\n        .force_empty_stream(\"geometries\")\n        .geo(poly_hole_touching())\n        .write(w, \"poly_hole_touching_fpf_tes\");\n\n    geo_varint()\n        .rings(E::rle_varint())\n        .rings2(E::rle_varint())\n        .geo(MultiPolygon(vec![poly1(), poly2()]))\n        .write(w, \"poly_multi\");\n    geo_fastpfor()\n        .rings(E::rle_fastpfor())\n        .rings2(E::rle_fastpfor())\n        .geo(MultiPolygon(vec![poly1(), poly2()]))\n        .write(w, \"poly_multi_fpf\");\n    geo_varint()\n        .rings(E::rle_varint())\n        .rings2(E::rle_varint())\n        .tessellate()\n        .geo(MultiPolygon(vec![poly1(), poly2()]))\n        .write(w, \"poly_multi_tes\");\n    geo_fastpfor()\n        .rings(E::rle_fastpfor())\n        .rings2(E::rle_fastpfor())\n        .tessellate()\n        .geo(MultiPolygon(vec![poly1(), poly2()]))\n        .write(w, \"poly_multi_fpf_tes\");\n\n    // Close the shared Morton curve into a ring to test Morton encoding for polygons.\n    let mut morton_ring = morton_curve();\n    morton_ring.push(morton_ring[0]);\n    let morton_poly = Polygon::new(LineString::new(morton_ring), vec![]);\n    geo_varint()\n        .geo(morton_poly.clone())\n        .write(w, \"poly_morton_ring_no_morton\");\n    geo_varint()\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .geo(morton_poly)\n        .write(w, \"poly_morton_ring_morton\");\n\n    // Split the Morton curve into two halves and close each into a ring to form a MultiPolygon.\n    let mc = morton_curve();\n    let half = mc.len() / 2;\n    let mut mr1 = mc[..half].to_vec();\n    mr1.push(mr1[0]);\n    let mut mr2 = mc[half..].to_vec();\n    mr2.push(mr2[0]);\n    let mp_morton = MultiPolygon(vec![\n        Polygon::new(LineString::new(mr1), vec![]),\n        Polygon::new(LineString::new(mr2), vec![]),\n    ]);\n    geo_varint()\n        .rings(E::rle_varint())\n        .rings2(E::rle_varint())\n        .geo(mp_morton.clone())\n        .write(w, \"poly_multi_morton_ring_no_morton\");\n    geo_varint()\n        .rings(E::rle_varint())\n        .rings2(E::rle_varint())\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .geo(mp_morton)\n        .write(w, \"poly_multi_morton_ring_morton\");\n\n    geo_varint()\n        .geo(MultiPoint(vec![P1, P2, P3]))\n        .write(w, \"multipoint\");\n    // Split the Morton curve at a different place so that the rings are different lengths,\n    // use one as the shell and one as the hole of a single and multi-polygon.\n    let quarter = mc.len() / 4;\n    let mut mr_shell = mc[..quarter].to_vec();\n    mr_shell.push(mr_shell[0]);\n    let mut mr_hole = mc[quarter..].to_vec();\n    mr_hole.push(mr_hole[0]);\n    let poly_with_hole = Polygon::new(LineString::new(mr_shell), vec![LineString::new(mr_hole)]);\n    geo_varint()\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .geo(poly_with_hole.clone())\n        .write(w, \"poly_morton_hole_morton\");\n    geo_varint()\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .geo(MultiPolygon(vec![poly_with_hole]))\n        .write(w, \"poly_multi_morton_hole_morton\");\n\n    geo_varint()\n        .no_rings(E::rle_varint())\n        .geo(MultiLineString(vec![line1(), line2()]))\n        .write(w, \"multiline\");\n\n    // Split the Morton curve into two halves to form a MultiLineString with Morton encoding.\n    let mline1 = LineString::new(mc[..half].to_vec());\n    let mline2 = LineString::new(mc[half..].to_vec());\n    geo_varint()\n        .no_rings(E::rle_varint())\n        .vertex_buffer_type(VertexBufferType::Morton)\n        .vertex_offsets(E::delta_rle_varint())\n        .geo(MultiLineString(vec![mline1, mline2]))\n        .write(w, \"multiline_morton\");\n}\n\nfn write_mix(w: &mut SynthWriter, current: &[usize]) {\n    let mut builder = geo_varint();\n    let mut builder_t = Some(geo_varint().tessellate());\n    let mut name = format!(\"mix_{}\", current.len());\n    for idx in current {\n        let mix_type = &MIX_TYPES[*idx];\n        builder = builder.geo(mix_type.1.clone());\n        write!(&mut name, \"_{}\", mix_type.0).unwrap();\n        if let Some(bldr) = builder_t {\n            if matches!(\n                mix_type.1,\n                Geometry::<i32>::Polygon(_) | Geometry::<i32>::MultiPolygon(_)\n            ) {\n                builder_t = Some(bldr.geo(mix_type.1.clone()));\n            } else {\n                builder_t = None;\n            }\n        }\n    }\n    if let Some(bldr) = builder_t {\n        // let suffix = if [\"...\"].contains(name) { \"\" } else { \"-rust\" };\n        let suffix = \"\";\n        bldr.force_empty_stream(\"geometries\")\n            .write(w, format!(\"{name}_tes{suffix}\"));\n    }\n    builder.write(w, &name);\n}\n\nfn generate_combinations(w: &mut SynthWriter, k: usize, start: usize, current: &mut Vec<usize>) {\n    if current.len() == k {\n        write_mix(w, current);\n    } else {\n        for i in start..MIX_TYPES.len() {\n            current.push(i);\n            generate_combinations(w, k, i + 1, current);\n            current.pop();\n        }\n    }\n}\n\nfn generate_mixed(w: &mut SynthWriter) {\n    // Generate all combinations of MIX_TYPES with length 2 or more\n    for k in 2..=MIX_TYPES.len() {\n        generate_combinations(w, k, 0, &mut Vec::new());\n    }\n    // Generate A-A (duplicate) and A-B-A patterns\n    for idx in 0..MIX_TYPES.len() {\n        write_mix(w, &[idx, idx]); // A-A variant\n        for idx2 in 0..MIX_TYPES.len() {\n            if idx != idx2 {\n                write_mix(w, &[idx, idx2, idx]); // A-B-A variant\n            }\n        }\n    }\n}\n\nfn generate_extent(w: &mut SynthWriter) {\n    for e in [512_i32, 4096, 131_072, 1_073_741_824] {\n        geo_varint()\n            .extent(e.cast_unsigned())\n            .geo(line![c(0_i32, 0), c(e - 1, e - 1)])\n            .write(w, format!(\"extent_{e}\"));\n        geo_varint()\n            .extent(e.cast_unsigned())\n            .geo(line![c(-42_i32, -42), c(e + 42, e + 42)])\n            .write(w, format!(\"extent_buf_{e}\"));\n    }\n}\n\nfn generate_ids(w: &mut SynthWriter) {\n    p0().ids(Id::u32(vec![100]), E::varint_with(L::None))\n        .write(w, \"id\");\n    p0().ids(Id::u32(vec![u32::MIN]), E::varint_with(L::None))\n        .write(w, \"id_min\");\n    p0().ids(Id::u32(vec![u32::MAX]), E::varint_with(L::None))\n        .write(w, \"id_max\");\n    p0().ids(Id::u64(vec![9_234_567_890]), E::varint_with(L::None))\n        .write(w, \"id64\");\n    p0().ids(Id::u64(vec![u64::MAX]), E::varint_with(L::None))\n        .write(w, \"id64_max\");\n\n    let four_p0 = || geo_varint_with_rle().geos([P0, P0, P0, P0]);\n    let dup_id = || Id::u32(vec![103; 4]);\n    let dup_u64_id = || Id::u64(vec![9_234_567_890; 4]);\n\n    four_p0()\n        .ids(dup_id(), E::varint_with(L::None))\n        .write(w, \"ids\");\n    four_p0()\n        .ids(dup_id(), E::varint_with(L::Delta))\n        .write(w, \"ids_delta\");\n    four_p0()\n        .ids(dup_id(), E::varint_with(L::Rle))\n        .write(w, \"ids_rle\");\n    four_p0()\n        .ids(dup_id(), E::varint_with(L::DeltaRle))\n        .write(w, \"ids_delta_rle\");\n    four_p0()\n        .ids(dup_u64_id(), E::varint_with(L::None))\n        .write(w, \"ids64\");\n    four_p0()\n        .ids(dup_u64_id(), E::varint_with(L::Delta))\n        .write(w, \"ids64_delta\");\n    four_p0()\n        .ids(dup_u64_id(), E::varint_with(L::Rle))\n        .write(w, \"ids64_rle\");\n    four_p0()\n        .ids(dup_u64_id(), E::varint_with(L::DeltaRle))\n        .write(w, \"ids64_delta_rle\");\n\n    let five_p0 = || geo_varint_with_rle().geos([P0, P0, P0, P0, P0]);\n    five_p0()\n        .ids(\n            Id::opt_u32(vec![Some(100), Some(101), None, Some(105), Some(106)]),\n            E::varint_with(L::None),\n        )\n        .write(w, \"ids_opt\");\n    five_p0()\n        .ids(\n            Id::opt_u32(vec![Some(100), Some(101), None, Some(105), Some(106)]),\n            E::varint_with(L::Delta),\n        )\n        .write(w, \"ids_opt_delta\");\n    five_p0()\n        .ids(\n            Id::opt_u64(vec![\n                None,\n                Some(9_234_567_890),\n                Some(101),\n                Some(105),\n                Some(106),\n            ]),\n            E::varint_with(L::None),\n        )\n        .write(w, \"ids64_opt\");\n    five_p0()\n        .ids(\n            Id::opt_u64(vec![\n                None,\n                Some(9_234_567_890),\n                Some(101),\n                Some(105),\n                Some(106),\n            ]),\n            E::varint_with(L::Delta),\n        )\n        .write(w, \"ids64_opt_delta\");\n\n    let min_max = || Id::u64(vec![u64::MIN, u64::MAX, u64::MIN, u64::MAX]);\n    four_p0()\n        .ids(min_max(), E::varint_with(L::None))\n        .write(w, \"ids64_minmax\");\n    four_p0()\n        .ids(min_max(), E::varint_with(L::Delta))\n        .write(w, \"ids64_minmax_delta\");\n\n    // FastPFOR physical encoding for u32 IDs (Rust-only: Java encoder does not support this)\n    four_p0().ids(dup_id(), E::fastpfor()).write(w, \"ids_fpf\");\n    four_p0()\n        .ids(dup_id(), E::delta_fastpfor())\n        .write(w, \"ids_delta_fpf\");\n}\n\nfn generate_properties(w: &mut SynthWriter) {\n    // Properties with special names\n    let e_any = E::varint();\n    p0().add_prop(e_any, P::bool(\"\", vec![true]))\n        .write(w, \"prop_empty_name_np\");\n    p0().add_prop(e_any, P::opt_bool(\"\", vec![Some(true)]))\n        .write(w, \"prop_empty_name\");\n    p0().add_prop(e_any, P::bool(\"hello\\u{0000} world\\n\", vec![true]))\n        .write(w, \"prop_special_name_np\");\n    p0().add_prop(\n        e_any,\n        P::opt_bool(\"hello\\u{0000} world\\n\", vec![Some(true)]),\n    )\n    .write(w, \"prop_special_name\");\n    p0().add_prop(e_any, P::bool(\"val\", vec![true]))\n        .write(w, \"prop_bool_np\");\n    p0().add_prop(e_any, P::opt_bool(\"val\", vec![Some(true)]))\n        .write(w, \"prop_bool\");\n    p0().add_prop(e_any, P::bool(\"val\", vec![false]))\n        .write(w, \"prop_bool_false_np\");\n    p0().add_prop(e_any, P::opt_bool(\"val\", vec![Some(false)]))\n        .write(w, \"prop_bool_false\");\n    // Two-feature optional bool variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_any, P::opt_bool(\"val\", vec![Some(true), None]))\n        .write(w, \"prop_bool_true_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_any, P::opt_bool(\"val\", vec![None, Some(true)]))\n        .write(w, \"prop_bool_null_true\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_any, P::opt_bool(\"val\", vec![Some(false), None]))\n        .write(w, \"prop_bool_false_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_any, P::opt_bool(\"val\", vec![None, Some(false)]))\n        .write(w, \"prop_bool_null_false\");\n\n    let e_int = E::varint();\n    p0().add_prop(e_int, P::i32(\"val\", vec![42]))\n        .write(w, \"prop_i32_np\");\n    p0().add_prop(e_int, P::opt_i32(\"val\", vec![Some(42)]))\n        .write(w, \"prop_i32\");\n    p0().add_prop(e_int, P::i32(\"val\", vec![-42]))\n        .write(w, \"prop_i32_neg_np\");\n    p0().add_prop(e_int, P::opt_i32(\"val\", vec![Some(-42)]))\n        .write(w, \"prop_i32_neg\");\n    p0().add_prop(e_int, P::i32(\"val\", vec![i32::MIN]))\n        .write(w, \"prop_i32_min_np\");\n    p0().add_prop(e_int, P::opt_i32(\"val\", vec![Some(i32::MIN)]))\n        .write(w, \"prop_i32_min\");\n    p0().add_prop(e_int, P::i32(\"val\", vec![i32::MAX]))\n        .write(w, \"prop_i32_max_np\");\n    p0().add_prop(e_int, P::opt_i32(\"val\", vec![Some(i32::MAX)]))\n        .write(w, \"prop_i32_max\");\n    // Two-feature optional i32 variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_i32(\"val\", vec![Some(42), None]))\n        .write(w, \"prop_i32_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_i32(\"val\", vec![None, Some(42)]))\n        .write(w, \"prop_i32_null_val\");\n\n    p0().add_prop(e_int, P::u32(\"val\", vec![42]))\n        .write(w, \"prop_u32_np\");\n    p0().add_prop(e_int, P::opt_u32(\"val\", vec![Some(42)]))\n        .write(w, \"prop_u32\");\n    p0().add_prop(e_int, P::u32(\"val\", vec![0]))\n        .write(w, \"prop_u32_min_np\");\n    p0().add_prop(e_int, P::opt_u32(\"val\", vec![Some(0)]))\n        .write(w, \"prop_u32_min\");\n    p0().add_prop(e_int, P::u32(\"val\", vec![u32::MAX]))\n        .write(w, \"prop_u32_max_np\");\n    p0().add_prop(e_int, P::opt_u32(\"val\", vec![Some(u32::MAX)]))\n        .write(w, \"prop_u32_max\");\n    // Two-feature optional u32 variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_u32(\"val\", vec![Some(42), None]))\n        .write(w, \"prop_u32_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_u32(\"val\", vec![None, Some(42)]))\n        .write(w, \"prop_u32_null_val\");\n\n    p0().add_prop(e_int, P::i64(\"val\", vec![9_876_543_210]))\n        .write(w, \"prop_i64_np\");\n    p0().add_prop(e_int, P::opt_i64(\"val\", vec![Some(9_876_543_210)]))\n        .write(w, \"prop_i64\");\n    p0().add_prop(e_int, P::i64(\"val\", vec![-9_876_543_210]))\n        .write(w, \"prop_i64_neg_np\");\n    p0().add_prop(e_int, P::opt_i64(\"val\", vec![Some(-9_876_543_210)]))\n        .write(w, \"prop_i64_neg\");\n    p0().add_prop(e_int, P::i64(\"val\", vec![i64::MIN]))\n        .write(w, \"prop_i64_min_np\");\n    p0().add_prop(e_int, P::opt_i64(\"val\", vec![Some(i64::MIN)]))\n        .write(w, \"prop_i64_min\");\n    p0().add_prop(e_int, P::i64(\"val\", vec![i64::MAX]))\n        .write(w, \"prop_i64_max_np\");\n    p0().add_prop(e_int, P::opt_i64(\"val\", vec![Some(i64::MAX)]))\n        .write(w, \"prop_i64_max\");\n    // Two-feature optional i64 variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_i64(\"val\", vec![Some(9_876_543_210), None]))\n        .write(w, \"prop_i64_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_int, P::opt_i64(\"val\", vec![None, Some(9_876_543_210)]))\n        .write(w, \"prop_i64_null_val\");\n\n    p0().add_prop(e_int, P::u64(\"bignum\", vec![1_234_567_890_123_456_789]))\n        .write(w, \"prop_u64_np\");\n    p0().add_prop(\n        e_int,\n        P::opt_u64(\"bignum\", vec![Some(1_234_567_890_123_456_789)]),\n    )\n    .write(w, \"prop_u64\");\n    p0().add_prop(e_int, P::u64(\"bignum\", vec![0]))\n        .write(w, \"prop_u64_min_np\");\n    p0().add_prop(e_int, P::opt_u64(\"bignum\", vec![Some(0)]))\n        .write(w, \"prop_u64_min\");\n    p0().add_prop(e_int, P::u64(\"bignum\", vec![u64::MAX]))\n        .write(w, \"prop_u64_max_np\");\n    p0().add_prop(e_int, P::opt_u64(\"bignum\", vec![Some(u64::MAX)]))\n        .write(w, \"prop_u64_max\");\n    // Two-feature optional u64 variants (key is \"val\" to match Java)\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(\n            e_int,\n            P::opt_u64(\"val\", vec![Some(1_234_567_890_123_456_789), None]),\n        )\n        .write(w, \"prop_u64_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(\n            e_int,\n            P::opt_u64(\"val\", vec![None, Some(1_234_567_890_123_456_789)]),\n        )\n        .write(w, \"prop_u64_null_val\");\n\n    let e_fl = E::varint();\n    #[expect(clippy::approx_constant)]\n    p0().add_prop(e_fl, P::f32(\"val\", vec![3.14]))\n        .write(w, \"prop_f32_np\");\n    #[expect(clippy::approx_constant)]\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(3.14)]))\n        .write(w, \"prop_f32\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::NEG_INFINITY]))\n        .write(w, \"prop_f32_neg_inf_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::NEG_INFINITY)]))\n        .write(w, \"prop_f32_neg_inf\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::from_bits(1)]))\n        .write(w, \"prop_f32_min_val_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::from_bits(1))]))\n        .write(w, \"prop_f32_min_val\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::MIN_POSITIVE]))\n        .write(w, \"prop_f32_min_norm_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::MIN_POSITIVE)]))\n        .write(w, \"prop_f32_min_norm\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![0.0]))\n        .write(w, \"prop_f32_zero_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(0.0)]))\n        .write(w, \"prop_f32_zero\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![-0.0]))\n        .write(w, \"prop_f32_neg_zero_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(-0.0)]))\n        .write(w, \"prop_f32_neg_zero\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::MAX]))\n        .write(w, \"prop_f32_max_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::MAX)]))\n        .write(w, \"prop_f32_max\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::INFINITY]))\n        .write(w, \"prop_f32_pos_inf_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::INFINITY)]))\n        .write(w, \"prop_f32_pos_inf\");\n    p0().add_prop(e_fl, P::f32(\"val\", vec![f32::NAN]))\n        .write(w, \"prop_f32_nan_np\");\n    p0().add_prop(e_fl, P::opt_f32(\"val\", vec![Some(f32::NAN)]))\n        .write(w, \"prop_f32_nan\");\n    // Two-feature optional f32 variants\n    #[expect(clippy::approx_constant)]\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_fl, P::opt_f32(\"val\", vec![Some(3.14), None]))\n        .write(w, \"prop_f32_val_null\");\n    #[expect(clippy::approx_constant)]\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_fl, P::opt_f32(\"val\", vec![None, Some(3.14)]))\n        .write(w, \"prop_f32_null_val\");\n\n    p0().add_prop(e_fl, P::f64(\"val\", vec![std::f64::consts::PI]))\n        .write(w, \"prop_f64_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(std::f64::consts::PI)]))\n        .write(w, \"prop_f64\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::NAN]))\n        .write(w, \"prop_f64_nan_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::NAN)]))\n        .write(w, \"prop_f64_nan\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::NEG_INFINITY]))\n        .write(w, \"prop_f64_neg_inf_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::NEG_INFINITY)]))\n        .write(w, \"prop_f64_neg_inf\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::from_bits(1)]))\n        .write(w, \"prop_f64_min_val_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::from_bits(1))]))\n        .write(w, \"prop_f64_min_val\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::MIN_POSITIVE]))\n        .write(w, \"prop_f64_min_norm_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::MIN_POSITIVE)]))\n        .write(w, \"prop_f64_min_norm\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![-0.0_f64]))\n        .write(w, \"prop_f64_neg_zero_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(-0.0_f64)]))\n        .write(w, \"prop_f64_neg_zero\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![0.0_f64]))\n        .write(w, \"prop_f64_zero_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(0.0_f64)]))\n        .write(w, \"prop_f64_zero\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::MAX]))\n        .write(w, \"prop_f64_max_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::MAX)]))\n        .write(w, \"prop_f64_max\");\n    p0().add_prop(e_fl, P::f64(\"val\", vec![f64::INFINITY]))\n        .write(w, \"prop_f64_pos_inf_np\");\n    p0().add_prop(e_fl, P::opt_f64(\"val\", vec![Some(f64::INFINITY)]))\n        .write(w, \"prop_f64_pos_inf\");\n    // Two-feature optional f64 variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(\n            e_fl,\n            P::opt_f64(\"val\", vec![Some(std::f64::consts::PI), None]),\n        )\n        .write(w, \"prop_f64_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(\n            e_fl,\n            P::opt_f64(\"val\", vec![None, Some(std::f64::consts::PI)]),\n        )\n        .write(w, \"prop_f64_null_val\");\n\n    let e_str = E::varint();\n    p0().add_prop(e_str, P::str(\"val\", [\"\"]))\n        .write(w, \"prop_str_empty_np\");\n    p0().add_prop(e_str, P::opt_str(\"val\", [Some(\"\")]))\n        .write(w, \"prop_str_empty\");\n    p0().add_prop(e_str, P::str(\"val\", [\"42\"]))\n        .write(w, \"prop_str_ascii_np\");\n    p0().add_prop(e_str, P::opt_str(\"val\", [Some(\"42\")]))\n        .write(w, \"prop_str_ascii\");\n    p0().add_prop(e_str, P::str(\"val\", [\"Line1\\n\\t\\\"quoted\\\"\\\\path\"]))\n        .write(w, \"prop_str_escape_np\");\n    p0().add_prop(\n        e_str,\n        P::opt_str(\"val\", [Some(\"Line1\\n\\t\\\"quoted\\\"\\\\path\")]),\n    )\n    .write(w, \"prop_str_escape\");\n    p0().add_prop(e_str, P::str(\"val\", [\"München 📍 cafe\\u{0301}\"]))\n        .write(w, \"prop_str_unicode_np\");\n    p0().add_prop(e_str, P::opt_str(\"val\", [Some(\"München 📍 cafe\\u{0301}\")]))\n        .write(w, \"prop_str_unicode\");\n    p0().add_prop(e_str, P::str(\"val\", [\"hello\\u{0000} world\\n\"]))\n        .write(w, \"prop_str_special_np\");\n    p0().add_prop(e_str, P::opt_str(\"val\", [Some(\"hello\\u{0000} world\\n\")]))\n        .write(w, \"prop_str_special\");\n    // Two-feature optional str variants\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_str, P::opt_str(\"val\", [Some(\"42\"), None]))\n        .write(w, \"prop_str_val_null\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_str, P::opt_str(\"val\", [None, Some(\"42\")]))\n        .write(w, \"prop_str_null_val\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_str, P::opt_str(\"val\", [Some(\"\"), None]))\n        .write(w, \"prop_str_val_empty\");\n    geo_varint_with_rle()\n        .geos([P0, P0])\n        .add_prop(e_str, P::opt_str(\"val\", [None, Some(\"\")]))\n        .write(w, \"prop_str_empty_val\");\n\n    p0().add_prop(E::varint(), P::bool(\"active\", vec![true]))\n        .add_prop(E::varint(), P::u64(\"biggest\", vec![0])) // FIXME: this should be u64, but java does it this way\n        .add_prop(E::varint(), P::i32(\"bignum\", vec![42]))\n        .add_prop(E::varint(), P::i32(\"count\", vec![42]))\n        .add_prop(E::varint(), P::u32(\"medium\", vec![100]))\n        .add_prop(E::varint(), P::str(\"name\", [\"Test Point\"]))\n        .add_prop(E::varint(), P::f64(\"precision\", vec![0.123_456_789]))\n        .add_prop(E::varint(), P::f32(\"temp\", vec![25.5]))\n        .write(w, \"props_mixed_np\");\n    p0().add_prop(E::varint(), P::opt_bool(\"active\", vec![Some(true)]))\n        .add_prop(E::varint(), P::opt_u64(\"biggest\", vec![Some(0)]))\n        .add_prop(E::varint(), P::opt_i32(\"bignum\", vec![Some(42)]))\n        .add_prop(E::varint(), P::opt_i32(\"count\", vec![Some(42)]))\n        .add_prop(E::varint(), P::opt_u32(\"medium\", vec![Some(100)]))\n        .add_prop(E::varint(), P::opt_str(\"name\", [Some(\"Test Point\")]))\n        .add_prop(\n            E::varint(),\n            P::opt_f64(\"precision\", vec![Some(0.123_456_789)]),\n        )\n        .add_prop(E::varint(), P::opt_f32(\"temp\", vec![Some(25.5)]))\n        .write(w, \"props_mixed\");\n\n    generate_props_i32(w);\n    generate_props_u32(w);\n    generate_props_u64(w);\n    generate_props_str(w);\n    generate_shared_dictionaries(w);\n}\n\nfn generate_props_i32(w: &mut SynthWriter) {\n    let four_points = || geo_varint_with_rle().geos([P0, P1, P2, P3]);\n    let values = || P::i32(\"val\", vec![42, 42, 42, 42]);\n    let opt_values = || P::opt_i32(\"val\", vec![Some(42), Some(42), Some(42), Some(42)]);\n\n    four_points()\n        .add_prop(E::varint(), values())\n        .write(w, \"props_i32_np\");\n    four_points()\n        .add_prop(E::varint(), opt_values())\n        .write(w, \"props_i32\");\n    four_points()\n        .add_prop(E::delta_varint(), values())\n        .write(w, \"props_i32_delta_np\");\n    four_points()\n        .add_prop(E::delta_varint(), opt_values())\n        .write(w, \"props_i32_delta\");\n    four_points()\n        .add_prop(E::rle_varint(), values())\n        .write(w, \"props_i32_rle_np\");\n    four_points()\n        .add_prop(E::rle_varint(), opt_values())\n        .write(w, \"props_i32_rle\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), values())\n        .write(w, \"props_i32_delta_rle_np\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), opt_values())\n        .write(w, \"props_i32_delta_rle\");\n}\n\nfn generate_props_u32(w: &mut SynthWriter) {\n    let four_points = || geo_varint_with_rle().geos([P0, P1, P2, P3]);\n    let values = || P::u32(\"val\", vec![9_000, 9_000, 9_000, 9_000]);\n    let opt_values = || {\n        P::opt_u32(\n            \"val\",\n            vec![Some(9_000), Some(9_000), Some(9_000), Some(9_000)],\n        )\n    };\n\n    four_points()\n        .add_prop(E::varint(), values())\n        .write(w, \"props_u32_np\");\n    four_points()\n        .add_prop(E::varint(), opt_values())\n        .write(w, \"props_u32\");\n    four_points()\n        .add_prop(E::delta_varint(), values())\n        .write(w, \"props_u32_delta_np\");\n    four_points()\n        .add_prop(E::delta_varint(), opt_values())\n        .write(w, \"props_u32_delta\");\n    four_points()\n        .add_prop(E::rle_varint(), values())\n        .write(w, \"props_u32_rle_np\");\n    four_points()\n        .add_prop(E::rle_varint(), opt_values())\n        .write(w, \"props_u32_rle\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), values())\n        .write(w, \"props_u32_delta_rle_np\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), opt_values())\n        .write(w, \"props_u32_delta_rle\");\n\n    for multiplier in [1, 2, 3, 4] {\n        for offset in [-1, 0, 1] {\n            let count = usize::try_from(128 * multiplier + offset).unwrap();\n            // Sequence 0,1,2, 0,1,2, 0,1,2, ...\n            let vals: Vec<u32> = (0..count).map(|i| u32::try_from(i % 3).unwrap()).collect();\n            let opt_vals: Vec<Option<u32>> = vals.iter().map(|&v| Some(v)).collect();\n            geo_fastpfor()\n                .meta(E::rle_fastpfor())\n                .geos(vec![P0; count])\n                .add_prop(E::fastpfor(), P::u32(\"val\", vals))\n                .write(w, format!(\"props_u32_fpf_{count}_np\"));\n            geo_fastpfor()\n                .meta(E::rle_fastpfor())\n                .geos(vec![P0; count])\n                .add_prop(E::fastpfor(), P::opt_u32(\"val\", opt_vals))\n                .write(w, format!(\"props_u32_fpf_{count}\"));\n        }\n    }\n}\n\nfn generate_props_u64(w: &mut SynthWriter) {\n    let four_points = || geo_varint_with_rle().geos([P0, P1, P2, P3]);\n    let property = || P::u64(\"val\", vec![9_000, 9_000, 9_000, 9_000]);\n    let opt_property = || {\n        P::opt_u64(\n            \"val\",\n            vec![Some(9_000), Some(9_000), Some(9_000), Some(9_000)],\n        )\n    };\n\n    four_points()\n        .add_prop(E::varint(), property())\n        .write(w, \"props_u64_np\");\n    four_points()\n        .add_prop(E::varint(), opt_property())\n        .write(w, \"props_u64\");\n    four_points()\n        .add_prop(E::delta_varint(), property())\n        .write(w, \"props_u64_delta_np\");\n    four_points()\n        .add_prop(E::delta_varint(), opt_property())\n        .write(w, \"props_u64_delta\");\n    four_points()\n        .add_prop(E::rle_varint(), property())\n        .write(w, \"props_u64_rle_np\");\n    four_points()\n        .add_prop(E::rle_varint(), opt_property())\n        .write(w, \"props_u64_rle\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), property())\n        .write(w, \"props_u64_delta_rle_np\");\n    four_points()\n        .add_prop(E::delta_rle_varint(), opt_property())\n        .write(w, \"props_u64_delta_rle\");\n}\n\nfn generate_props_str(w: &mut SynthWriter) {\n    let six_points = || geo_varint_with_rle().geos([P1, P2, P3, PH1, PH2, PH3]);\n    let str_vals = [\n        \"residential_zone_north_sector_1\",\n        \"commercial_zone_south_sector_2\",\n        \"industrial_zone_east_sector_3\",\n        \"park_zone_west_sector_4\",\n        \"water_zone_north_sector_5\",\n        \"residential_zone_south_sector_6\",\n    ];\n    // _np variant: non-optional (CT::Str, no presence stream)\n    six_points()\n        .add_prop(E::varint(), P::str(\"val\", str_vals))\n        .write(w, \"props_str_np\");\n    // canonical: all-present optional (CT::OptStr + presence), matching Java's format\n    six_points()\n        .add_prop(E::varint(), P::opt_str(\"val\", str_vals.map(Some)))\n        .write(w, \"props_str\");\n    // FSST variants — same split\n    six_points()\n        .add_prop_str_fsst(E::varint(), E::varint(), P::str(\"val\", str_vals))\n        .write(w, \"props_str_fsst_np\");\n    six_points()\n        .add_prop_str_fsst(\n            E::varint(),\n            E::varint(),\n            P::opt_str(\"val\", str_vals.map(Some)),\n        )\n        .write(w, \"props_str_fsst\"); // FSST compression output is not byte-for-byte consistent with Java's\n\n    // Two features with the same 30-char value → deduplicated dictionary encoding.\n    // 30 chars because otherwise FSST is skipped.\n    let long_string = || \"A\".repeat(30);\n    let two_pts = || geo_varint_with_rle().geos([P1, P2]);\n\n    two_pts()\n        .add_prop_str_dict(\n            E::varint(),\n            E::rle_varint(),\n            P::str(\"val\", [long_string(), long_string()]),\n        )\n        .write(w, \"props_offset_str_np\");\n    two_pts()\n        .add_prop_str_dict(\n            E::varint(),\n            E::rle_varint(),\n            P::opt_str(\"val\", [Some(long_string()), Some(long_string())]),\n        )\n        .write(w, \"props_offset_str\");\n    two_pts()\n        .add_prop_str_fsst_dict(\n            E::varint(),\n            E::varint(),\n            E::rle_varint(),\n            P::str(\"val\", [long_string(), long_string()]),\n        )\n        .write(w, \"props_offset_str_fsst_np\");\n    two_pts()\n        .add_prop_str_fsst_dict(\n            E::varint(),\n            E::varint(),\n            E::rle_varint(),\n            P::opt_str(\"val\", [Some(long_string()), Some(long_string())]),\n        )\n        .write(w, \"props_offset_str_fsst\");\n}\n\nfn generate_shared_dictionaries(w: &mut SynthWriter) {\n    let long_string = || \"A\".repeat(30);\n    let e_str = E::varint();\n    p0().add_prop(e_str, P::str(\"name:de\", [long_string()]))\n        .add_prop(e_str, P::str(\"name:en\", [long_string()]))\n        .write(w, \"props_no_shared_dict_np\");\n    p0().add_prop(e_str, P::opt_str(\"name:de\", [Some(long_string())]))\n        .add_prop(e_str, P::opt_str(\"name:en\", [Some(long_string())]))\n        .write(w, \"props_no_shared_dict\");\n\n    p0().add_shared_dict(\n        SharedDict::new(\"name:\", StrEncoding::Plain)\n            .col(\"de\", E::varint(), [long_string()])\n            .col(\"en\", E::varint(), [long_string()]),\n    )\n    .write(w, \"props_shared_dict_np\");\n    p0().add_shared_dict(\n        SharedDict::new(\"name:\", StrEncoding::Plain)\n            .opt(\"de\", E::varint(), [Some(long_string())])\n            .opt(\"en\", E::varint(), [Some(long_string())]),\n    )\n    .write(w, \"props_shared_dict\");\n\n    p0().add_shared_dict(\n        SharedDict::new(\"\", StrEncoding::Plain)\n            .col(\"a\", E::varint(), [long_string()])\n            .col(\"b\", E::varint(), [long_string()]),\n    )\n    .write(w, \"props_shared_dict_no_struct_name_np\");\n    p0().add_shared_dict(\n        SharedDict::new(\"\", StrEncoding::Plain)\n            .opt(\"a\", E::varint(), [Some(long_string())])\n            .opt(\"b\", E::varint(), [Some(long_string())]),\n    )\n    .write(w, \"props_shared_dict_no_struct_name\");\n    p0().add_shared_dict(\n        SharedDict::new(\"\", StrEncoding::Fsst)\n            .col(\"a\", E::varint(), [long_string()])\n            .col(\"b\", E::varint(), [long_string()]),\n    )\n    .write(w, \"props_shared_dict_no_struct_name_fsst_np\");\n    p0().add_shared_dict(\n        SharedDict::new(\"\", StrEncoding::Fsst)\n            .opt(\"a\", E::varint(), [Some(long_string())])\n            .opt(\"b\", E::varint(), [Some(long_string())]),\n    )\n    .write(w, \"props_shared_dict_no_struct_name_fsst\");\n\n    p0().add_prop(e_str, P::str(\"place\", [long_string()]))\n        .add_shared_dict(SharedDict::new(\"name:en\", StrEncoding::Plain).col(\n            \"\",\n            E::varint(),\n            [long_string()],\n        ))\n        .write(w, \"props_shared_dict_one_child_np\");\n    p0().add_prop(e_str, P::opt_str(\"place\", [Some(long_string())]))\n        .add_shared_dict(SharedDict::new(\"name:en\", StrEncoding::Plain).opt(\n            \"\",\n            E::varint(),\n            [Some(long_string())],\n        ))\n        .write(w, \"props_shared_dict_one_child\");\n\n    p0().add_shared_dict(SharedDict::new(\"a\", StrEncoding::Plain).col(\n        \"\",\n        E::varint(),\n        [long_string()],\n    ))\n    .write(w, \"props_shared_dict_no_child_name_np\");\n    p0().add_shared_dict(SharedDict::new(\"a\", StrEncoding::Plain).opt(\n        \"\",\n        E::varint(),\n        [Some(long_string())],\n    ))\n    .write(w, \"props_shared_dict_no_child_name\");\n\n    p0().add_shared_dict(\n        SharedDict::new(\"name:\", StrEncoding::Fsst)\n            .col(\"de\", E::varint(), [long_string()])\n            .col(\"en\", E::varint(), [long_string()]),\n    )\n    .write(w, \"props_shared_dict_fsst_np\");\n    p0().add_shared_dict(\n        SharedDict::new(\"name:\", StrEncoding::Fsst)\n            .opt(\"de\", E::varint(), [Some(long_string())])\n            .opt(\"en\", E::varint(), [Some(long_string())]),\n    )\n    .write(w, \"props_shared_dict_fsst\");\n\n    p0().add_shared_dict(SharedDict::new(\"a\", StrEncoding::Fsst).col(\n        \"\",\n        E::varint(),\n        [long_string()],\n    ))\n    .write(w, \"props_shared_dict_no_child_name_fsst_np\");\n    p0().add_shared_dict(SharedDict::new(\"a\", StrEncoding::Fsst).opt(\n        \"\",\n        E::varint(),\n        [Some(long_string())],\n    ))\n    .write(w, \"props_shared_dict_no_child_name_fsst\");\n\n    p0().add_prop(e_str, P::str(\"place\", [long_string()]))\n        .add_shared_dict(SharedDict::new(\"name:en\", StrEncoding::Fsst).col(\n            \"\",\n            E::varint(),\n            [long_string()],\n        ))\n        .write(w, \"props_shared_dict_one_child_fsst_np\");\n    p0().add_prop(e_str, P::opt_str(\"place\", [Some(long_string())]))\n        .add_shared_dict(SharedDict::new(\"name:en\", StrEncoding::Fsst).opt(\n            \"\",\n            E::varint(),\n            [Some(long_string())],\n        ))\n        .write(w, \"props_shared_dict_one_child_fsst\");\n    p0()\n        // column names MUST be unique, but the shared dict prefix can duplicate\n        // Note that Java sorts column names for some reason\n        .add_shared_dict(\n            SharedDict::new(\"name\", StrEncoding::Plain)\n                .col(\":de\", E::varint(), [long_string()])\n                .col(\"_en\", E::varint(), [long_string()]),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"name\", StrEncoding::Plain)\n                .col(\":he\", E::varint(), [long_string()])\n                .col(\"_fr\", E::varint(), [long_string()]),\n        )\n        .write(w, \"props_shared_dict_2_same_prefix_np\");\n    p0().add_shared_dict(\n        SharedDict::new(\"name\", StrEncoding::Plain)\n            .opt(\":de\", E::varint(), [Some(long_string())])\n            .opt(\"_en\", E::varint(), [Some(long_string())]),\n    )\n    .add_shared_dict(\n        SharedDict::new(\"name\", StrEncoding::Plain)\n            .opt(\":he\", E::varint(), [Some(long_string())])\n            .opt(\"_fr\", E::varint(), [Some(long_string())]),\n    )\n    .write(w, \"props_shared_dict_2_same_prefix\");\n\n    let mixed = || [Some(long_string()), None, Some(long_string())];\n    let all = || [long_string(), long_string(), long_string()];\n    let all_opt = || all().map(Some);\n    let none = || [None::<String>, None::<String>, None::<String>];\n\n    geo_varint_with_rle()\n        .geos([P0, P0, P0])\n        .add_shared_dict(\n            SharedDict::new(\"1-\", StrEncoding::Plain)\n                .col(\"a\", E::varint(), all())\n                .col(\"b\", E::varint(), all()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"2-\", StrEncoding::Plain)\n                .col(\"a\", E::varint(), all())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"3-\", StrEncoding::Plain)\n                .col(\"a\", E::varint(), all())\n                .opt(\"b\", E::varint(), none()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"4-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .col(\"b\", E::varint(), all()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"5-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"6-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .opt(\"b\", E::varint(), none()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"7-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), none())\n                .col(\"b\", E::varint(), all()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"8-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), none())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .write(w, \"props_shared_dict_presence_variants_np\");\n\n    // canonical: all columns are optional (presence stream always written).\n    geo_varint_with_rle()\n        .geos([P0, P0, P0])\n        .add_shared_dict(\n            SharedDict::new(\"1-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), all_opt())\n                .opt(\"b\", E::varint(), all_opt()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"2-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), all_opt())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"3-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), all_opt())\n                .opt(\"b\", E::varint(), none()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"4-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .opt(\"b\", E::varint(), all_opt()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"5-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"6-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), mixed())\n                .opt(\"b\", E::varint(), none()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"7-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), none())\n                .opt(\"b\", E::varint(), all_opt()),\n        )\n        .add_shared_dict(\n            SharedDict::new(\"8-\", StrEncoding::Plain)\n                .opt(\"a\", E::varint(), none())\n                .opt(\"b\", E::varint(), mixed()),\n        )\n        .write(w, \"props_shared_dict_presence_variants\");\n}\n"
  },
  {
    "path": "rust/mlt-synthetics/src/writer.rs",
    "content": "use std::collections::HashSet;\nuse std::fs;\nuse std::io::Write as _;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr as _;\n\nuse mlt_core::geojson::FeatureCollection;\nuse mlt_core::{Decoder, MltError, Parser};\n\nuse crate::Args;\nuse crate::layer::Layer;\n\npub struct SynthWriter {\n    ref_dir: PathBuf,\n    out_dir: PathBuf,\n    verbose: bool,\n    generated: HashSet<String>,\n    rust_written: usize,\n    notes: usize,\n    pub failures: usize,\n}\n\npub type SynthResult<T> = Result<T, SynthErr>;\n\n#[derive(Debug, thiserror::Error)]\npub enum SynthErr {\n    #[error(transparent)]\n    Mlt(#[from] MltError),\n    #[error(\"cannot read reference MLT file: {0}\")]\n    ReadRefMlt(#[source] std::io::Error),\n    #[error(\"MLT mismatch: reference file {} does not match generated content. Content saved to -rust dir.\", .0.display())]\n    MltMismatch(PathBuf),\n    #[error(\"cannot read reference JSON file: {0}\")]\n    ReadRefJson(#[source] std::io::Error),\n    #[error(\"decoded JSON differs from reference\")]\n    JsonMismatch,\n    #[error(\"cannot parse reference as FeatureCollection: {0}\")]\n    UnparsableRef(serde_json::Error),\n    #[error(\"cannot compare FeatureCollections: {0}\")]\n    CannotCompare(serde_json::Error),\n    #[error(\"cannot serialize FeatureCollection: {0}\")]\n    SerializeJson(serde_json::Error),\n    #[error(\"cannot write {0}: {1}\")]\n    WriteFile(PathBuf, #[source] std::io::Error),\n}\n\n/// Compare `actual` against the JSON reference file at `ref_path`.\n/// Returns `Ok(())` on match, or a typed `SynthError` on I/O error, parse failure, or mismatch.\npub fn check_json(actual: &FeatureCollection, ref_path: &Path) -> SynthResult<()> {\n    let ref_json = fs::read_to_string(ref_path).map_err(SynthErr::ReadRefJson)?;\n    let expected = FeatureCollection::from_str(&ref_json).map_err(SynthErr::UnparsableRef)?;\n    if actual.equals(&expected).map_err(SynthErr::CannotCompare)? {\n        Ok(())\n    } else {\n        Err(SynthErr::JsonMismatch)\n    }\n}\n\npub fn write_file(path: &Path, data: &[u8]) -> SynthResult<()> {\n    Layer::open_new(path)\n        .and_then(|mut f| f.write_all(data))\n        .map_err(|source| SynthErr::WriteFile(path.to_path_buf(), source))\n}\n\npub fn decode_to_json(bytes: &[u8]) -> FeatureCollection {\n    let mut dec = Decoder::default();\n    let decoded = dec\n        .decode_all(Parser::default().parse_layers(bytes).unwrap())\n        .unwrap();\n    FeatureCollection::from_layers(decoded).unwrap()\n}\n\nimpl SynthWriter {\n    pub fn new(mut args: Args) -> Self {\n        let canonical_synth = args.synthetics.canonicalize();\n        let canonical_synth = canonical_synth.unwrap_or_else(|e| {\n            panic!(\n                \"reference synthetics dir not found: {}\\n{e}\",\n                args.synthetics.display()\n            )\n        });\n        args.synthetics = canonical_synth;\n\n        println!(\"Verifying synthetics against {}\", args.synthetics.display());\n        println!(\n            \"Writing rust-only files to {}\",\n            args.synthetics_rust.display()\n        );\n\n        fs::create_dir_all(&args.synthetics_rust)\n            .unwrap_or_else(|e| panic!(\"cannot create {}: {e}\", args.synthetics_rust.display()));\n\n        Self {\n            ref_dir: args.synthetics,\n            out_dir: args.synthetics_rust,\n            verbose: args.verbose,\n            failures: 0,\n            generated: HashSet::new(),\n            rust_written: 0,\n            notes: 0,\n        }\n    }\n\n    pub fn print_note(&mut self, msg: &str) {\n        self.notes += 1;\n        eprintln!(\"Note: {msg}\");\n    }\n\n    /// Encode and write (or verify) `layer`, recording the outcome in this writer's statistics.\n    pub fn write(&mut self, layer: Layer, name: impl AsRef<str>) {\n        let name = name.as_ref();\n        let res = self.write_int(layer, name);\n        match res {\n            Ok(is_rust) => {\n                let typ = if is_rust {\n                    self.rust_written += 1;\n                    // Record the base name so report_ungenerated won't warn about\n                    // ref files that are covered by a rust-only counterpart.\n                    if let Some(base) = name.strip_suffix(\"-rust\") {\n                        self.generated.insert(base.to_string());\n                    }\n                    \"wrote\"\n                } else {\n                    assert!(\n                        self.generated.insert(name.to_string()),\n                        \"duplicate generated name: {name}\"\n                    );\n                    \"ok\"\n                };\n                if self.verbose {\n                    println!(\"{typ:5}  {name}\");\n                }\n            }\n            Err(e) => {\n                eprintln!(\"FAIL {name}: {e}\");\n                self.failures += 1;\n            }\n        }\n    }\n\n    /// Encode `layer` and either verify (shared files) or write (rust-only files).\n    ///\n    /// Returns `Ok(true)` for a rust-only file, `Ok(false)` for a shared file,\n    /// or `Err` on any failure.\n    fn write_int(&mut self, layer: Layer, mut name: &str) -> SynthResult<bool> {\n        let mut is_rust_specific = false;\n        if let Some(base) = name.strip_suffix(\"-rust\") {\n            is_rust_specific = true;\n            name = base;\n        }\n        if name.contains(\"_fsst\") {\n            // FSST frequently generates binary-different but compatible data\n            is_rust_specific = true;\n        }\n        let name_mlt = format!(\"{name}.mlt\");\n        let name_json = format!(\"{name}.json\");\n        let rust_mlt = self.out_dir.join(&name_mlt);\n        let rust_json = self.out_dir.join(&name_json);\n        let ref_mlt = self.ref_dir.join(&name_mlt);\n        let ref_json = self.ref_dir.join(&name_json);\n        let ref_json_exists = ref_json.is_file();\n        let bytes = layer.encode_to_bytes()?;\n        let decoded = decode_to_json(&bytes);\n\n        if is_rust_specific || !ref_json_exists {\n            // rust-only: write MLT to disk, compare decoded JSON to reference (if it exists).\n            write_file(&rust_mlt, &bytes)?;\n            if ref_json_exists {\n                check_json(&decoded, &ref_json)?;\n            } else {\n                self.print_note(&format!(\n                    \"Java synthetics doesn't have MLT matching 0x01-rust/{name_mlt}\"\n                ));\n            }\n            let mut s = serde_json::to_string_pretty(&decoded).map_err(SynthErr::SerializeJson)?;\n            s.push('\\n');\n            write_file(&rust_json, s.as_bytes())?;\n            Ok(true)\n        } else {\n            // shared: verify bytes and JSON against reference, nothing written to disk.\n            fs::read(&ref_mlt)\n                .map_err(SynthErr::ReadRefMlt)\n                .and_then(|ref_bytes| {\n                    if ref_bytes == bytes {\n                        Ok(())\n                    } else {\n                        write_file(&rust_mlt, &bytes)?;\n                        Err(SynthErr::MltMismatch(ref_mlt))\n                    }\n                })?;\n            check_json(&decoded, &ref_json)?;\n            Ok(false)\n        }\n    }\n\n    /// Warn about `.mlt` files in the reference dir that Rust never generated.\n    /// Prints a summary that includes the total failure count.\n    pub fn report_ungenerated(&mut self) {\n        let mut ref_mlts: Vec<String> = fs::read_dir(&self.ref_dir)\n            .unwrap_or_else(|e| panic!(\"cannot read {}: {e}\", self.ref_dir.display()))\n            .flatten()\n            .filter_map(|e| {\n                let p = e.path();\n                (p.extension()? == \"mlt\")\n                    .then(|| p.file_stem().unwrap().to_string_lossy().into_owned())\n            })\n            .collect();\n        ref_mlts.sort();\n\n        for name in &ref_mlts {\n            if !self.generated.contains(name) {\n                self.print_note(&format!(\n                    \"Rust synthetics did not generate a test matching Java's 0x01/{name}.mlt\"\n                ));\n            }\n        }\n\n        println!(\n            \"Verified: {} | Rust-only: {} | Notes: {} | Failures: {}\",\n            self.generated.len(),\n            self.rust_written,\n            self.notes,\n            self.failures,\n        );\n    }\n}\n"
  },
  {
    "path": "rust/mlt-wasm/.gitignore",
    "content": "# wasm-pack output\npkg/\n\n# TypeScript compiler output\ndist/\n\n# npm\nnode_modules/\n"
  },
  {
    "path": "rust/mlt-wasm/.nvmrc",
    "content": "24.11\n"
  },
  {
    "path": "rust/mlt-wasm/CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.1.6](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.5...rust-mlt-wasm-v0.1.6) - 2026-04-29\n\n### Other\n\n- *(rust)* rm \"01\" from TileLayer01, StagedLayer01 ([#1345](https://github.com/maplibre/maplibre-tile-spec/pull/1345))\n\n## [0.1.5](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.4...rust-mlt-wasm-v0.1.5) - 2026-04-18\n\n### Fixed\n\n- *(rust)* geo builds on wasm, remove unnecessary feature gates ([#1297](https://github.com/maplibre/maplibre-tile-spec/pull/1297))\n\n### Other\n\n- *(rust)* rm RawStreamData and EncodedStreamData ([#1309](https://github.com/maplibre/maplibre-tile-spec/pull/1309))\n- *(rust)* Coord32 cleanup, dep update ([#1304](https://github.com/maplibre/maplibre-tile-spec/pull/1304))\n- *(rust)* update geo, simplify tessellation ([#1305](https://github.com/maplibre/maplibre-tile-spec/pull/1305))\n- *(java)* Extend synthetic tests to include rings ([#1292](https://github.com/maplibre/maplibre-tile-spec/pull/1292))\n- Add offline docs.rs-style workspace docs check to Rust CI and fix surfaced rustdoc links ([#1295](https://github.com/maplibre/maplibre-tile-spec/pull/1295))\n\n## [0.1.4](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.3...rust-mlt-wasm-v0.1.4) - 2026-04-13\n\n### Other\n\n- *(rust)* move frames/v01 -> decoder, adj use ([#1246](https://github.com/maplibre/maplibre-tile-spec/pull/1246))\n- *(rust)* mv tessellation to core ([#1220](https://github.com/maplibre/maplibre-tile-spec/pull/1220))\n- more tessellated synthetics ([#1218](https://github.com/maplibre/maplibre-tile-spec/pull/1218))\n- *(rust)* implement feature/property iterator and more type state ([#1198](https://github.com/maplibre/maplibre-tile-spec/pull/1198))\n\n## [0.1.3](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.2...rust-mlt-wasm-v0.1.3) - 2026-03-23\n\n### Other\n\n- *(rust)* migrate to Rust fastpfor ([#1190](https://github.com/maplibre/maplibre-tile-spec/pull/1190))\n\n## [0.1.2](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.1...rust-mlt-wasm-v0.1.2) - 2026-03-17\n\n### Other\n\n- *(rust)* memory budgeting, codecs  ([#1168](https://github.com/maplibre/maplibre-tile-spec/pull/1168))\n- *(rust)* introduce EncDec decode states ([#1166](https://github.com/maplibre/maplibre-tile-spec/pull/1166))\n- *(rust)* add stateful decoder ([#1163](https://github.com/maplibre/maplibre-tile-spec/pull/1163))\n- *(rust)* rename to IdValues and GeometryValues ([#1159](https://github.com/maplibre/maplibre-tile-spec/pull/1159))\n- *(rust)* mv impls out of models, use full wire round-trips ([#1158](https://github.com/maplibre/maplibre-tile-spec/pull/1158))\n- *(rust)* rework WASM code to use TileLayer ([#1153](https://github.com/maplibre/maplibre-tile-spec/pull/1153))\n- *(rust)* remove unnecessary to_owned calls ([#1151](https://github.com/maplibre/maplibre-tile-spec/pull/1151))\n- *(rust)* introduce staging types in Rust layer implementation ([#1149](https://github.com/maplibre/maplibre-tile-spec/pull/1149))\n- *(rust)* introduce staging types ([#1148](https://github.com/maplibre/maplibre-tile-spec/pull/1148))\n- *(rust)* refactor parsing and encoding code ([#1144](https://github.com/maplibre/maplibre-tile-spec/pull/1144))\n- *(rust)* get rid of borrowme, add EncDec enum ([#1141](https://github.com/maplibre/maplibre-tile-spec/pull/1141))\n- *(rust)* simplify ID model ([#1139](https://github.com/maplibre/maplibre-tile-spec/pull/1139))\n- *(rust)* make wasm bench more stable ([#1120](https://github.com/maplibre/maplibre-tile-spec/pull/1120))\n- *(rust)* move name into DecodedStrings ([#1108](https://github.com/maplibre/maplibre-tile-spec/pull/1108))\n\n## [0.1.1](https://github.com/maplibre/maplibre-tile-spec/compare/rust-mlt-wasm-v0.1.0...rust-mlt-wasm-v0.1.1) - 2026-03-10\n\n### Other\n\n- updated the following local packages: mlt-core\n"
  },
  {
    "path": "rust/mlt-wasm/Cargo.toml",
    "content": "[package]\nname = \"mlt-wasm\"\ndescription = \"WebAssembly bindings for the MapLibre Tile (MLT) format\"\nversion = \"0.1.6\"\nauthors.workspace = true\ncategories.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nkeywords.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[package.metadata.wasm-pack.profile.release]\nwasm-opt = [\"-O3\", \"--enable-simd\", \"--enable-bulk-memory\"]\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\njs-sys.workspace = true\nmlt-core.workspace = true\nwasm-bindgen.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "rust/mlt-wasm/README.md",
    "content": "# mlt-wasm\n\nWebAssembly bindings for the [MapLibre Tile (MLT)](https://github.com/maplibre/maplibre-tile-spec) decoder.\n\nCompiles `mlt-core` to WASM via [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) and ships a TypeScript wrapper exposing a `VectorTileLike` API.\n\n## Layout\n\n```\nmlt-wasm/\n├── src/lib.rs         # wasm-bindgen bindings\n├── js/\n│   ├── index.ts       # package entry point\n│   └── vectorTile.ts  # VectorTileLike wrapper\n├── pkg/               # wasm-pack output (gitignored)\n├── dist/              # tsc output (gitignored)\n├── Cargo.toml\n├── package.json\n└── tsconfig.json\n```\n\n## Build\n\nRequires [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) and Node.js.\n\n```sh\nnpm run build\n```\n\nThis runs `wasm-pack build --target bundler --out-dir pkg` followed by `tsc`. Both steps are also available individually:\n\n```sh\nnpm run build:wasm\nnpm run build:ts\n```\n\n## Usage\n\n```ts\nimport { decodeTile } from '@maplibre/mlt-wasm';\n\nconst data = new Uint8Array(await fetch(tileUrl).then(r => r.arrayBuffer()));\nconst tile = decodeTile(data);\n\nfor (const [name, layer] of Object.entries(tile.layers)) {\n    for (let i = 0; i < layer.length; i++) {\n        const feature = layer.feature(i);\n        console.log(feature.type);           // 1 | 2 | 3\n        console.log(feature.id);             // number | undefined\n        console.log(feature.properties);     // fetched lazily from WASM\n        console.log(feature.loadGeometry()); // Point[][]\n    }\n}\n```\n"
  },
  {
    "path": "rust/mlt-wasm/js/decoder.bench.ts",
    "content": "import { readdirSync, readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { VectorTileLike } from \"@maplibre/vt-pbf\";\nimport { beforeAll, bench, describe } from \"vitest\";\nimport tsDecodeTile from \"../../ts/src/mltDecoder\";\nimport type FeatureTable from \"../../ts/src/vector/featureTable\";\nimport { decodeTile as wasmDecodeTile } from \"./vectorTile\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst OMT = resolve(__dirname, \"../../../test/expected/tag0x01/omt\");\n\nfunction loadPool(zoom: number): Uint8Array[] {\n  return readdirSync(OMT)\n    .filter((f) => f.startsWith(`${zoom}_`) && f.endsWith(\".mlt\"))\n    .sort()\n    .map((f) => new Uint8Array(readFileSync(resolve(OMT, f))));\n}\n\n// zoom 11: 12 tiles, 39–89 KB each  (~760 KB total — exceeds typical L2)\n// zoom 10: 12 tiles, 70–128 KB each (~1.1 MB total)\n// zoom 14: 10 tiles, 344–763 KB each (~5.5 MB total — exceeds most L3s)\nconst POOLS = [\n  { label: \"small  (zoom 11, 39–89 KB)\", pool: loadPool(11) },\n  { label: \"medium (zoom 10, 70–128 KB)\", pool: loadPool(10) },\n  { label: \"large  (zoom 14, 344–763 KB)\", pool: loadPool(14) },\n];\n\nfunction traverseWasm(tile: VectorTileLike): number {\n  let n = 0;\n  for (const layer of Object.values(tile.layers)) {\n    for (let i = 0; i < layer.length; i++) {\n      const f = layer.feature(i);\n      void f.properties;\n      f.loadGeometry();\n      n++;\n    }\n  }\n  return n;\n}\n\nfunction traverseTs(tables: FeatureTable[]): number {\n  let n = 0;\n  for (const table of tables) {\n    const geometries = table.geometryVector.getGeometries();\n    for (let i = 0; i < table.numFeatures; i++) {\n      void geometries[i];\n      for (const col of table.propertyVectors) {\n        if (col) col.getValue(i);\n      }\n      n++;\n    }\n  }\n  return n;\n}\n\n// Requires Node to be started with --expose-gc (done by the bench npm script); no-op otherwise.\nfunction drainGC(): void {\n  const _gc = (globalThis as { gc?: () => void }).gc;\n  if (typeof _gc === \"function\") {\n    _gc();\n    _gc();\n  }\n}\n\nconst OPTIONS = {\n  warmupTime: 500,\n  time: 2000,\n  minSamples: 40,\n} as const;\n\nfor (const { label, pool } of POOLS) {\n  describe(`decode + traverse - ${label}`, () => {\n    let ti = 0;\n    let wi = 0;\n\n    // Drain heap garbage left by the previous describe block so GC does not\n    // fire at an unpredictable point inside this block's measurement window.\n    beforeAll(drainGC);\n\n    bench(\n      \"TS decoder\",\n      () => {\n        const tables = tsDecodeTile(pool[ti++ % pool.length]);\n        if (traverseTs(tables) < 0) throw new Error(\"unreachable\");\n      },\n      OPTIONS,\n    );\n\n    bench(\n      \"WASM decoder\",\n      () => {\n        const tile = wasmDecodeTile(pool[wi++ % pool.length]);\n        if (traverseWasm(tile) < 0) throw new Error(\"unreachable\");\n      },\n      OPTIONS,\n    );\n  });\n}\n"
  },
  {
    "path": "rust/mlt-wasm/js/index.ts",
    "content": "export type {\n  VectorTileFeatureLike,\n  VectorTileLayerLike,\n  VectorTileLike,\n} from \"@maplibre/vt-pbf\";\nexport { decodeTile } from \"./vectorTile\";\n"
  },
  {
    "path": "rust/mlt-wasm/js/synthetic.spec.ts",
    "content": "import { readFile } from \"node:fs/promises\";\nimport type { VectorTileLike } from \"@maplibre/vt-pbf\";\nimport { describe, expect, it } from \"vitest\";\nimport {\n  compareWithTolerance,\n  getTestCases,\n  writeActualOutput,\n} from \"../../../test/synthetic/synthetic-test-utils\";\nimport {\n  decodeTile,\n  type MltFeature,\n  MltGeometryType,\n  type MltLayer,\n} from \"./vectorTile\";\n\nconst UNIMPLEMENTED_SYNTHETICS = new Map([\n  [\"poly_collinear_fpf\", \"FastPFor not supported\"],\n  [\"poly_collinear_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_fpf\", \"FastPFor not supported\"],\n  [\"poly_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_hole_fpf\", \"FastPFor not supported\"],\n  [\"poly_hole_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_hole_touching_fpf\", \"FastPFor not supported\"],\n  [\"poly_hole_touching_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_multi_fpf\", \"FastPFor not supported\"],\n  [\"poly_multi_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_self_intersect_fpf\", \"FastPFor not supported\"],\n  [\"poly_self_intersect_fpf_tes\", \"FastPFor not supported\"],\n  [\"poly_multi_morton_hole_morton\", \"Pending investigation\"],\n]);\n\ndescribe(\"MLT WASM Decoder - Synthetic tests\", () => {\n  expect.addEqualityTesters([compareWithTolerance]);\n  const testCases = getTestCases(Array.from(UNIMPLEMENTED_SYNTHETICS.keys()));\n\n  for (const { name, content, fileName } of testCases.active) {\n    it(name, async () => {\n      const actual = await decodeMLT(fileName);\n      writeActualOutput(fileName, actual);\n      expect(actual).toEqual(content);\n    });\n  }\n\n  for (const skippedTest of testCases.skipped) {\n    it.skip(skippedTest, () => {\n      // Test is skipped since it is not supported yet\n    });\n  }\n});\n\nasync function decodeMLT(\n  mltFilePath: string,\n): Promise<Record<string, unknown>> {\n  const mltBuffer = await readFile(mltFilePath);\n  const tile = decodeTile(new Uint8Array(mltBuffer));\n  return tileToFeatureCollection(tile) as unknown as Record<string, unknown>;\n}\n\nfunction tileToFeatureCollection(\n  tile: VectorTileLike,\n): GeoJSON.FeatureCollection {\n  const features: GeoJSON.Feature[] = [];\n  for (const layer of Object.values(tile.layers)) {\n    const mltLayer = layer as MltLayer;\n\n    for (let i = 0; i < mltLayer.length; i++) {\n      const feature = mltLayer.feature(i);\n      const properties: Record<string, number | string | boolean> = {\n        _layer: mltLayer.name,\n        _extent: mltLayer.extent,\n      };\n\n      for (let k = 0; k < mltLayer.propertyKeys.length; k++) {\n        const key = mltLayer.propertyKeys[k];\n        const col = mltLayer.propertyColumns[k];\n        let val = feature.properties[key];\n\n        if (typeof val === \"number\") {\n          if (Number.isNaN(val)) {\n            if (col instanceof Float32Array) val = \"f32::NAN\";\n            else if (col instanceof Float64Array) val = \"f64::NAN\";\n          } else if (val === Infinity) {\n            if (col instanceof Float32Array) val = \"f32::INFINITY\";\n            else if (col instanceof Float64Array) val = \"f64::INFINITY\";\n          } else if (val === -Infinity) {\n            if (col instanceof Float32Array) val = \"f32::NEG_INFINITY\";\n            else if (col instanceof Float64Array) val = \"f64::NEG_INFINITY\";\n          }\n        }\n        if (val !== undefined) {\n          properties[key] = val;\n        }\n      }\n\n      const geojsonFeature: GeoJSON.Feature = {\n        type: \"Feature\",\n        geometry: getGeometry(feature),\n        properties,\n      };\n      if (feature.id !== undefined) {\n        geojsonFeature.id = feature.id;\n      }\n      features.push(geojsonFeature);\n    }\n  }\n  return { type: \"FeatureCollection\", features };\n}\n\nfunction getGeometry(feature: MltFeature): GeoJSON.Geometry {\n  const rings = feature.loadGeometry();\n  const coords = rings.map((ring) => ring.map((p) => [p.x, p.y]));\n  switch (feature.mltType) {\n    case MltGeometryType.Point:\n      return { type: \"Point\", coordinates: coords[0][0] };\n    case MltGeometryType.MultiPoint:\n      return { type: \"MultiPoint\", coordinates: coords.map((r) => r[0]) };\n    case MltGeometryType.LineString:\n      return { type: \"LineString\", coordinates: coords[0] };\n    case MltGeometryType.MultiLineString:\n      return { type: \"MultiLineString\", coordinates: coords };\n    case MltGeometryType.Polygon: {\n      const polygons = feature.loadPolygons();\n      return {\n        type: \"Polygon\",\n        coordinates: polygons[0].map((ring) =>\n          closeRing(ring.map((p) => [p.x, p.y])),\n        ),\n      };\n    }\n    case MltGeometryType.MultiPolygon: {\n      const polygons = feature.loadPolygons();\n      return {\n        type: \"MultiPolygon\",\n        coordinates: polygons.map((polygon) =>\n          polygon.map((ring) => closeRing(ring.map((p) => [p.x, p.y]))),\n        ),\n      };\n    }\n    default:\n      throw new Error(`Unsupported MLT geometry type: ${feature.mltType}`);\n  }\n}\n\n/** GeoJSON polygons must have their first and last coordinate identical. */\nfunction closeRing(ring: number[][]): number[][] {\n  if (ring.length === 0) return ring;\n  const first = ring[0];\n  const last = ring[ring.length - 1];\n  if (first[0] !== last[0] || first[1] !== last[1]) {\n    return [...ring, [first[0], first[1]]];\n  }\n  return ring;\n}\n"
  },
  {
    "path": "rust/mlt-wasm/js/vectorTile.ts",
    "content": "import Point from \"@mapbox/point-geometry\";\nimport type {\n  VectorTileFeatureLike,\n  VectorTileLayerLike,\n  VectorTileLike,\n} from \"@maplibre/vt-pbf\";\nimport { decode_tile as wasmDecodeTile } from \"../pkg/mlt_wasm.js\";\n\n// ---------------------------------------------------------------------------\n// WASM interface\n// ---------------------------------------------------------------------------\n\ninterface LayerGeometry {\n  /** Cumulative offsets into part_offsets (multi-geometry types only). Zero-length otherwise. */\n  geometry_offsets(): Uint32Array;\n  /** Cumulative offsets into ring_offsets or directly into vertices. Zero-length for pure Point layers. */\n  part_offsets(): Uint32Array;\n  /** Cumulative vertex-count offsets. Zero-length when no ring-level indirection is needed. */\n  ring_offsets(): Uint32Array;\n  /** Flat [x0, y0, x1, y1, …] vertex buffer in tile coordinates. */\n  vertices(): Int32Array;\n}\n\ninterface WasmMltTile {\n  layer_count(): number;\n  layer_name(layer_idx: number): string;\n  layer_extent(layer_idx: number): number;\n  feature_count(layer_idx: number): number;\n  /** Bulk MVT geometry types for the whole layer as a Uint8Array (one byte per feature: 1/2/3). */\n  layer_types(layer_idx: number): Uint8Array;\n  /** Original MLT geometry types (0=Point, 1=LineString, 2=Polygon, 3=MultiPoint, 4=MultiLineString, 5=MultiPolygon). */\n  layer_mlt_types(layer_idx: number): Uint8Array;\n  /**\n   * Bulk IDs for the whole layer as a Float64Array (one f64 per feature).\n   * NaN when the feature has no ID.\n   */\n  layer_ids(layer_idx: number): Float64Array;\n  /**\n   * All decoded geometry arrays for the layer in one call.\n   * JS walks these directly — zero WASM calls per feature for geometry.\n   */\n  layer_geometry(layer_idx: number): LayerGeometry;\n  /** Column names for the layer, parallel to layer_properties(). */\n  layer_property_keys(layer_idx: number): string[];\n  /**\n   * All property values as an array of columns, parallel to layer_property_keys().\n   * Each column is a typed array (numeric) or plain Array (bool/string) of\n   * length feature_count. Index i gives the value for feature i; absent values\n   * are NaN (numeric) or undefined (bool/string).\n   */\n  layer_properties(\n    layer_idx: number,\n  ): Array<\n    | Int8Array\n    | Uint8Array\n    | Int32Array\n    | Uint32Array\n    | Float32Array\n    | Float64Array\n    | Array<boolean | string | undefined>\n  >;\n  feature_properties(\n    layer_idx: number,\n    feature_idx: number,\n  ): Record<string, number | string | boolean>;\n  free(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Geometry type enums\n// ---------------------------------------------------------------------------\n\n// MVT geometry types (VectorTileFeatureLike.type)\nconst POINT = 1;\nconst LINESTRING = 2;\nconst POLYGON = 3;\n\n/** Mirrors `GeometryType` in mlt-core — preserves the single vs multi distinction that MVT collapses. */\nexport enum MltGeometryType {\n  Point = 0,\n  LineString = 1,\n  Polygon = 2,\n  MultiPoint = 3,\n  MultiLineString = 4,\n  MultiPolygon = 5,\n}\n\n// ---------------------------------------------------------------------------\n// loadGeometry — JS equivalent of DecodedGeometry::to_mvt_rings\n// ---------------------------------------------------------------------------\n\nfunction openRing(verts: Int32Array, start: number, end: number): Point[] {\n  const ring: Point[] = new Array(end - start) as Point[];\n  for (let i = start; i < end; i++) {\n    ring[i - start] = new Point(verts[i * 2], verts[i * 2 + 1]);\n  }\n  return ring;\n}\n\nfunction loadGeometry(\n  mvtType: number,\n  featureIdx: number,\n  geomOffsets: Uint32Array,\n  partOffsets: Uint32Array,\n  ringOffsets: Uint32Array,\n  verts: Int32Array,\n): Point[][] {\n  const hasGeomOffsets = geomOffsets.length > 0;\n  const hasPartOffsets = partOffsets.length > 0;\n  const hasRingOffsets = ringOffsets.length > 0;\n\n  if (mvtType === POINT) {\n    if (!hasGeomOffsets) {\n      // Mixed-type layers may have part/ring indirection even for points.\n      let idx = featureIdx;\n      if (hasPartOffsets) idx = partOffsets[idx];\n      if (hasRingOffsets) idx = ringOffsets[idx];\n      return [[new Point(verts[idx * 2], verts[idx * 2 + 1])]];\n    } else {\n      const gStart = geomOffsets[featureIdx];\n      const gEnd = geomOffsets[featureIdx + 1];\n      const rings: Point[][] = new Array(gEnd - gStart) as Point[][];\n      for (let g = gStart; g < gEnd; g++) {\n        let idx = g;\n        if (hasPartOffsets) idx = partOffsets[idx];\n        if (hasRingOffsets) idx = ringOffsets[idx];\n        rings[g - gStart] = [new Point(verts[idx * 2], verts[idx * 2 + 1])];\n      }\n      return rings;\n    }\n  }\n\n  if (mvtType === LINESTRING) {\n    if (!hasGeomOffsets) {\n      let start: number;\n      let end: number;\n      if (hasRingOffsets) {\n        const partIdx = partOffsets[featureIdx];\n        start = ringOffsets[partIdx];\n        end = ringOffsets[partIdx + 1];\n      } else {\n        start = partOffsets[featureIdx];\n        end = partOffsets[featureIdx + 1];\n      }\n      return [openRing(verts, start, end)];\n    } else {\n      const gStart = geomOffsets[featureIdx];\n      const gEnd = geomOffsets[featureIdx + 1];\n      const result: Point[][] = new Array(gEnd - gStart) as Point[][];\n      for (let g = gStart; g < gEnd; g++) {\n        let start: number;\n        let end: number;\n        if (hasRingOffsets) {\n          const partIdx = partOffsets[g];\n          start = ringOffsets[partIdx];\n          end = ringOffsets[partIdx + 1];\n        } else {\n          start = partOffsets[g];\n          end = partOffsets[g + 1];\n        }\n        result[g - gStart] = openRing(verts, start, end);\n      }\n      return result;\n    }\n  }\n\n  if (mvtType === POLYGON) {\n    if (!hasGeomOffsets) {\n      const partStart = partOffsets[featureIdx];\n      const partEnd = partOffsets[featureIdx + 1];\n      const rings: Point[][] = new Array(partEnd - partStart) as Point[][];\n      for (let r = partStart; r < partEnd; r++) {\n        rings[r - partStart] = openRing(\n          verts,\n          ringOffsets[r],\n          ringOffsets[r + 1],\n        );\n      }\n      return rings;\n    } else {\n      // Flat ring list matching MVT convention — use loadPolygons() for grouped output.\n      const gStart = geomOffsets[featureIdx];\n      const gEnd = geomOffsets[featureIdx + 1];\n      const result: Point[][] = [];\n      for (let g = gStart; g < gEnd; g++) {\n        const partStart = partOffsets[g];\n        const partEnd = partOffsets[g + 1];\n        for (let r = partStart; r < partEnd; r++) {\n          result.push(openRing(verts, ringOffsets[r], ringOffsets[r + 1]));\n        }\n      }\n      return result;\n    }\n  }\n\n  return [];\n}\n\n/** Returns rings grouped by polygon using offset arrays instead of winding-order heuristics. */\nfunction loadPolygons(\n  featureIdx: number,\n  geomOffsets: Uint32Array,\n  partOffsets: Uint32Array,\n  ringOffsets: Uint32Array,\n  verts: Int32Array,\n): Point[][][] {\n  if (geomOffsets.length === 0) {\n    const partStart = partOffsets[featureIdx];\n    const partEnd = partOffsets[featureIdx + 1];\n    const rings: Point[][] = new Array(partEnd - partStart) as Point[][];\n    for (let r = partStart; r < partEnd; r++) {\n      rings[r - partStart] = openRing(\n        verts,\n        ringOffsets[r],\n        ringOffsets[r + 1],\n      );\n    }\n    return [rings];\n  }\n\n  const gStart = geomOffsets[featureIdx];\n  const gEnd = geomOffsets[featureIdx + 1];\n  const polygons: Point[][][] = new Array(gEnd - gStart) as Point[][][];\n  for (let g = gStart; g < gEnd; g++) {\n    const partStart = partOffsets[g];\n    const partEnd = partOffsets[g + 1];\n    const rings: Point[][] = new Array(partEnd - partStart) as Point[][];\n    for (let r = partStart; r < partEnd; r++) {\n      rings[r - partStart] = openRing(\n        verts,\n        ringOffsets[r],\n        ringOffsets[r + 1],\n      );\n    }\n    polygons[g - gStart] = rings;\n  }\n  return polygons;\n}\n\n// ---------------------------------------------------------------------------\n// MltFeature\n// ---------------------------------------------------------------------------\n\nexport class MltFeature implements VectorTileFeatureLike {\n  readonly extent: number;\n\n  private _type: 0 | 1 | 2 | 3 | undefined;\n  private _id: number | undefined | null;\n\n  constructor(\n    private readonly _featureIdx: number,\n    extent: number,\n    private readonly _types: Uint8Array,\n    private readonly _mltTypes: Uint8Array,\n    private readonly _ids: Float64Array,\n    private readonly _geomOffsets: Uint32Array,\n    private readonly _partOffsets: Uint32Array,\n    private readonly _ringOffsets: Uint32Array,\n    private readonly _verts: Int32Array,\n    private readonly propertyKeys: string[],\n    private readonly propertyColumns: Array<\n      | Int8Array\n      | Uint8Array\n      | Int32Array\n      | Uint32Array\n      | Float32Array\n      | Float64Array\n      | Array<boolean | string | undefined>\n    >,\n  ) {\n    this.extent = extent;\n    this._id = null;\n  }\n\n  get mltType(): MltGeometryType {\n    return this._mltTypes[this._featureIdx] as MltGeometryType;\n  }\n\n  get type(): 0 | 1 | 2 | 3 {\n    if (this._type === undefined) {\n      this._type = this._types[this._featureIdx] as 0 | 1 | 2 | 3;\n    }\n    return this._type;\n  }\n\n  get id(): number | undefined {\n    if (this._id === null) {\n      const raw = this._ids[this._featureIdx];\n      this._id = Number.isNaN(raw) ? undefined : raw;\n    }\n    return this._id as number | undefined;\n  }\n\n  get properties(): Record<string, number | string | boolean> {\n    const result: Record<string, number | string | boolean> = {};\n    for (let k = 0; k < this.propertyKeys.length; k++) {\n      const col = this.propertyColumns[k];\n      const val = col[this._featureIdx];\n      if (val !== undefined) {\n        result[this.propertyKeys[k]] = val as number | string | boolean;\n      }\n    }\n    return result;\n  }\n\n  loadGeometry(): Point[][] {\n    return loadGeometry(\n      this.type,\n      this._featureIdx,\n      this._geomOffsets,\n      this._partOffsets,\n      this._ringOffsets,\n      this._verts,\n    );\n  }\n\n  /** Returns rings grouped by polygon — avoids the lossy winding-order heuristic in MVT's classifyRings. */\n  loadPolygons(): Point[][][] {\n    if (this.type !== POLYGON) return [this.loadGeometry()];\n    return loadPolygons(\n      this._featureIdx,\n      this._geomOffsets,\n      this._partOffsets,\n      this._ringOffsets,\n      this._verts,\n    );\n  }\n}\n\n// ---------------------------------------------------------------------------\n// MltLayer\n// ---------------------------------------------------------------------------\n\nexport class MltLayer implements VectorTileLayerLike {\n  readonly version = 1 as const;\n  readonly name: string;\n  readonly extent: number;\n  readonly length: number;\n\n  private readonly _types: Uint8Array;\n  private readonly _mltTypes: Uint8Array;\n  private readonly _ids: Float64Array;\n  private readonly _geomOffsets: Uint32Array;\n  private readonly _partOffsets: Uint32Array;\n  private readonly _ringOffsets: Uint32Array;\n  private readonly _verts: Int32Array;\n  readonly propertyKeys: string[];\n  readonly propertyColumns: Array<\n    | Int8Array\n    | Uint8Array\n    | Int32Array\n    | Uint32Array\n    | Float32Array\n    | Float64Array\n    | Array<boolean | string | undefined>\n  >;\n\n  constructor(\n    readonly _tile: WasmMltTile,\n    readonly _layerIdx: number,\n    name: string,\n  ) {\n    this.name = name;\n    this.extent = _tile.layer_extent(_layerIdx);\n    this.length = _tile.feature_count(_layerIdx);\n    this._types = _tile.layer_types(_layerIdx);\n    this._mltTypes = _tile.layer_mlt_types(_layerIdx);\n    this._ids = _tile.layer_ids(_layerIdx);\n    const geom = _tile.layer_geometry(_layerIdx);\n    this._geomOffsets = geom.geometry_offsets();\n    this._partOffsets = geom.part_offsets();\n    this._ringOffsets = geom.ring_offsets();\n    this._verts = geom.vertices();\n    this.propertyKeys = _tile.layer_property_keys(_layerIdx);\n    this.propertyColumns = _tile.layer_properties(_layerIdx);\n  }\n\n  feature(i: number): MltFeature {\n    return new MltFeature(\n      i,\n      this.extent,\n      this._types,\n      this._mltTypes,\n      this._ids,\n      this._geomOffsets,\n      this._partOffsets,\n      this._ringOffsets,\n      this._verts,\n      this.propertyKeys,\n      this.propertyColumns,\n    );\n  }\n}\n\nexport function decodeTile(data: Uint8Array): VectorTileLike {\n  const tile = wasmDecodeTile(data) as WasmMltTile;\n  const layers: Record<string, VectorTileLayerLike> = {};\n  for (let i = 0; i < tile.layer_count(); i++) {\n    const name = tile.layer_name(i);\n    layers[name] = new MltLayer(tile, i, name);\n  }\n  return { layers };\n}\n"
  },
  {
    "path": "rust/mlt-wasm/package.json",
    "content": "{\n  \"name\": \"@maplibre/mlt-wasm\",\n  \"version\": \"0.1.6\",\n  \"description\": \"WebAssembly-backed MapLibre Tile (MLT) decoder\",\n  \"type\": \"module\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"files\": [\n    \"/dist\",\n    \"/pkg\"\n  ],\n  \"license\": \"(MIT OR Apache-2.0)\",\n  \"homepage\": \"https://github.com/maplibre/maplibre-tile-spec/#readme\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/maplibre/maplibre-tile-spec\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/maplibre/maplibre-tile-spec/issues\"\n  },\n  \"keywords\": [\n    \"maplibre\",\n    \"mlt\",\n    \"vector-tile\",\n    \"wasm\",\n    \"webassembly\",\n    \"gis\"\n  ],\n  \"scripts\": {\n    \"build:wasm\": \"RUSTFLAGS=\\\"-Zunstable-options -Cpanic=immediate-abort -Ctarget-feature=+simd128\\\" CARGO_UNSTABLE_BUILD_STD=std,panic_abort RUSTUP_TOOLCHAIN=nightly wasm-pack build --target bundler --weak-refs --out-dir pkg\",\n    \"build:wasm-gz\": \"gzip --best --no-name pkg/mlt_wasm_bg.wasm --keep --force && du -sh pkg/*.wasm*\",\n    \"build:ts\": \"tsc\",\n    \"build\": \"npm run build:wasm && npm run build:ts && npm run build:wasm-gz\",\n    \"test\": \"vitest --coverage --coverage.reportOnFailure --run\",\n    \"test:watch\": \"vitest --coverage --coverage.reportOnFailure --ui\",\n    \"bench\": \"node --expose-gc --no-liftoff --no-wasm-tier-up node_modules/.bin/vitest bench\"\n  },\n  \"dependencies\": {\n    \"@mapbox/point-geometry\": \"1.1.0\"\n  },\n  \"devDependencies\": {\n    \"@maplibre/vt-pbf\": \"4.3.0\",\n    \"@types/node\": \"^24.10.2\",\n    \"@vitest/coverage-v8\": \"^4.0.1\",\n    \"@vitest/ui\": \"^4.0.18\",\n    \"typescript\": \"^5.9.3\",\n    \"vite-plugin-top-level-await\": \"^1.6.0\",\n    \"vite-plugin-wasm\": \"^3.5.0\",\n    \"vitest\": \"^4.0.1\"\n  }\n}\n"
  },
  {
    "path": "rust/mlt-wasm/src/geometry.rs",
    "content": "use js_sys::{Int32Array, Uint32Array};\nuse mlt_core::GeometryValues;\nuse wasm_bindgen::prelude::*;\n\n/// All decoded geometry arrays for a single layer, fetched in one WASM call.\n///\n/// JS indexes into these typed arrays directly inside `loadGeometry()`, so\n/// there are **zero** per-feature WASM boundary crossings for geometry.\n///\n/// ## Array semantics (mirrors `GeometryValues`)\n///\n/// All offset arrays are cumulative: `offsets[i]` is the start index and\n/// `offsets[i+1]` is the exclusive end for feature/part/ring `i`.\n/// Vertex indices count whole vertices (pairs), so vertex `n` lives at\n/// `vertices[n*2]`, `vertices[n*2+1]`.\n///\n/// | Getter             | Present for                                               |\n/// |--------------------|-----------------------------------------------------------|\n/// | `geometry_offsets` | `MultiPoint`, `MultiLineString`, `MultiPolygon`           |\n/// | `part_offsets`     | `LineString`, `Polygon`, `MultiLineString`, `MultiPolygon`|\n/// | `ring_offsets`     | `Polygon`, `MultiPolygon` (+ `LineString` when mixed)     |\n/// | `vertices`         | always                                                    |\n///\n/// Absent offset arrays are returned as zero-length `Uint32Array`s so JS can\n/// always branch on `.length` without a null-check.\n#[wasm_bindgen]\npub struct LayerGeometry {\n    pub(crate) geometry_offsets: Uint32Array,\n    pub(crate) part_offsets: Uint32Array,\n    pub(crate) ring_offsets: Uint32Array,\n    pub(crate) vertices: Int32Array,\n}\n\n#[wasm_bindgen]\nimpl LayerGeometry {\n    /// Cumulative offsets into `part_offsets` for multi-geometry types.\n    /// Zero-length when no multi-geometry features are present.\n    #[must_use]\n    pub fn geometry_offsets(&self) -> Uint32Array {\n        self.geometry_offsets.clone()\n    }\n\n    /// Cumulative offsets into `ring_offsets` (or directly into `vertices`\n    /// for `LineString` layers without rings).\n    /// Zero-length for pure Point layers.\n    #[must_use]\n    pub fn part_offsets(&self) -> Uint32Array {\n        self.part_offsets.clone()\n    }\n\n    /// Cumulative offsets into the vertex buffer (counting whole vertices).\n    /// Zero-length when no ring-level indirection is needed.\n    #[must_use]\n    pub fn ring_offsets(&self) -> Uint32Array {\n        self.ring_offsets.clone()\n    }\n\n    /// Flat vertex buffer: `[x0, y0, x1, y1, …]` in tile coordinates.\n    #[must_use]\n    pub fn vertices(&self) -> Int32Array {\n        self.vertices.clone()\n    }\n}\n\nimpl LayerGeometry {\n    /// Build a [`LayerGeometry`] from a decoded [`GeometryValues`].\n    pub(crate) fn from_values(geom: &GeometryValues) -> Self {\n        let geometry_offsets = geom\n            .geometry_offsets()\n            .map_or_else(|| Uint32Array::new_with_length(0), Uint32Array::from);\n\n        let part_offsets = geom\n            .part_offsets()\n            .map_or_else(|| Uint32Array::new_with_length(0), Uint32Array::from);\n\n        let ring_offsets = geom\n            .ring_offsets()\n            .map_or_else(|| Uint32Array::new_with_length(0), Uint32Array::from);\n\n        let vertices = geom\n            .vertices()\n            .map_or_else(|| Int32Array::new_with_length(0), Int32Array::from);\n\n        Self {\n            geometry_offsets,\n            part_offsets,\n            ring_offsets,\n            vertices,\n        }\n    }\n}\n"
  },
  {
    "path": "rust/mlt-wasm/src/layer.rs",
    "content": "use mlt_core::{GeometryValues, TileLayer};\n\n/// All per-layer state owned by [`crate::tile::MltTile`].\n///\n/// Fully decoded at `decode_tile` time — no lazy loading.\npub(crate) struct DecodedLayer {\n    pub(crate) tile: TileLayer,\n\n    /// MVT geometry types (0/1/2/3) — collapses single and multi variants.\n    pub(crate) types_array: js_sys::Uint8Array,\n\n    /// Original MLT geometry types — preserves the single vs multi distinction.\n    pub(crate) mlt_types_array: js_sys::Uint8Array,\n\n    pub(crate) geometry: GeometryValues,\n}\n"
  },
  {
    "path": "rust/mlt-wasm/src/lib.rs",
    "content": "//! WebAssembly bindings for the `MapLibre` Tile (MLT) format.\n//!\n//! # Design\n//!\n//! A single `MltTile` struct owns all decoded [`mlt_core::TileLayer`] data for\n//! every layer in the tile.  No per-layer or per-feature WASM objects are\n//! created; every accessor takes explicit `(layer_idx, feature_idx)` arguments\n//! so the JavaScript side can keep plain numeric indices rather than\n//! heap-allocated wrapper objects.\n//!\n//! ## Geometry\n//!\n//! `MltTile::layer_geometry` returns a `LayerGeometry`\n//! whose typed-array getters expose the raw offset and vertex buffers.\n//! JS walks these directly — zero WASM boundary crossings per feature.\n//!\n//! ## IDs\n//!\n//! `MltTile::layer_ids` returns a `Float64Array` — one `f64` per\n//! feature.  Absent IDs are `NaN` (≡ `undefined` after the JS wrapper checks\n//! `isNaN`).  IDs above `Number.MAX_SAFE_INTEGER` lose precision.\n//!\n//! ## Properties\n//!\n//! `MltTile::layer_property_keys` and `MltTile::layer_properties`\n//! expose all property columns as typed arrays built once per layer.  JS reads\n//! any feature's property with a single array index — zero WASM calls during\n//! traversal.\n\nmod geometry;\nmod layer;\nmod properties;\nmod tile;\n\nuse js_sys::Uint8Array;\nuse layer::DecodedLayer;\nuse mlt_core::{Decoder, GeometryType, MltError, Parser};\nuse tile::MltTile;\nuse wasm_bindgen::prelude::*;\n\n/// Decode a raw MLT tile blob and return an `MltTile`.\n///\n/// All geometry, IDs and properties are decoded eagerly into row-oriented\n/// [`mlt_core::TileLayer`] values.\n#[wasm_bindgen]\npub fn decode_tile(data: &[u8]) -> Result<MltTile, JsError> {\n    let mut parser = Parser::default();\n    let raw_layers = parser.parse_layers(data).map_err(|e| to_js_err(&e))?;\n    let mut dec = Decoder::default();\n    let mut layers = Vec::with_capacity(raw_layers.len());\n\n    for raw_layer in raw_layers {\n        // Skip non-Tag01 layers.\n        let mlt_core::Layer::Tag01(layer01) = raw_layer else {\n            continue;\n        };\n\n        // Decode all columns at once, then extract geometry arrays before consuming into tile.\n        let parsed_layer = layer01.decode_all(&mut dec).map_err(|e| to_js_err(&e))?;\n\n        // Clone geometry values for building WASM typed arrays (zero wire-decode overhead:\n        // geometry is already in columnar form from decode_all).\n        let parsed_geometry = parsed_layer.geometry_values().clone();\n\n        let (types_bytes, mlt_types_bytes): (Vec<u8>, Vec<u8>) = parsed_geometry\n            .vector_types()\n            .iter()\n            .map(|t| {\n                let mvt = match t {\n                    GeometryType::Point | GeometryType::MultiPoint => 1,\n                    GeometryType::LineString | GeometryType::MultiLineString => 2,\n                    GeometryType::Polygon | GeometryType::MultiPolygon => 3,\n                    #[allow(unreachable_patterns)]\n                    _ => 0,\n                };\n                (mvt, *t as u8)\n            })\n            .unzip();\n        let types_array = Uint8Array::from(types_bytes.as_slice());\n        let mlt_types_array = Uint8Array::from(mlt_types_bytes.as_slice());\n\n        let tile = parsed_layer\n            .into_tile(&mut dec)\n            .map_err(|e| to_js_err(&e))?;\n\n        layers.push(DecodedLayer {\n            tile,\n            types_array,\n            mlt_types_array,\n            geometry: parsed_geometry,\n        });\n    }\n\n    Ok(MltTile { layers })\n}\n\npub(crate) fn to_js_err(e: &MltError) -> JsError {\n    JsError::new(&e.to_string())\n}\n"
  },
  {
    "path": "rust/mlt-wasm/src/properties.rs",
    "content": "use js_sys::{Array, Float64Array, Int8Array, Int32Array, Uint8Array, Uint32Array};\nuse mlt_core::{PropValue, TileLayer};\nuse wasm_bindgen::prelude::*;\n\n/// Cached bulk-property data for a single layer.\npub(crate) struct PropCache {\n    /// One JS string per logical column, parallel to `columns`.\n    pub(crate) keys: Array,\n\n    /// One typed array (or plain `Array`) per logical column, parallel to `keys`.\n    /// Each array has length `feature_count`; index `i` is the value for feature `i`.\n    pub(crate) columns: Array,\n}\n\n/// Build a [`PropCache`] from a fully decoded [`TileLayer`].\npub(crate) fn build_prop_cache(tile: &TileLayer) -> PropCache {\n    let n = tile.features.len();\n    let keys = Array::new();\n    let columns = Array::new();\n\n    for (col_idx, name) in tile.property_names.iter().enumerate() {\n        keys.push(&JsValue::from_str(name));\n        columns.push(&build_column(tile, col_idx, n));\n    }\n\n    PropCache { keys, columns }\n}\n\nfn feature_count_u32(n: usize) -> u32 {\n    u32::try_from(n).expect(\"feature count fits in u32\")\n}\n\nfn idx_u32(i: usize) -> u32 {\n    u32::try_from(i).expect(\"index fits in u32\")\n}\n\n#[allow(clippy::cast_precision_loss)]\nfn build_column(tile: &TileLayer, col_idx: usize, n: usize) -> JsValue {\n    // Peek at the first feature to determine the column variant.\n    let first = tile\n        .features\n        .first()\n        .and_then(|f| f.properties.get(col_idx));\n\n    match first {\n        Some(PropValue::Bool(_)) => {\n            let arr = Array::new_with_length(feature_count_u32(n));\n            for (i, f) in tile.features.iter().enumerate() {\n                if let Some(PropValue::Bool(Some(b))) = f.properties.get(col_idx) {\n                    arr.set(idx_u32(i), JsValue::from_bool(*b));\n                }\n            }\n            arr.into()\n        }\n        Some(PropValue::I8(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::I8(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::I8(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(f64::from(*v)));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<i8> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::I8(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Int8Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::U8(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::U8(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::U8(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(f64::from(*v)));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<u8> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::U8(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Uint8Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::I32(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::I32(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::I32(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(f64::from(*v)));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<i32> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::I32(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Int32Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::U32(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::U32(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::U32(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(f64::from(*v)));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<u32> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::U32(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Uint32Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::I64(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::I64(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::I64(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(*v as f64));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<f64> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::I64(v)) = f.properties.get(col_idx) {\n                            v.map(|n| n as f64)\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Float64Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::U64(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::U64(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::U64(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(*v as f64));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<f64> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::U64(v)) = f.properties.get(col_idx) {\n                            v.map(|n| n as f64)\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Float64Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::F32(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::F32(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::F32(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(f64::from(*v)));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<f32> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::F32(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                js_sys::Float32Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::F64(_)) => {\n            let any_none = tile\n                .features\n                .iter()\n                .any(|f| matches!(f.properties.get(col_idx), Some(PropValue::F64(None))));\n            if any_none {\n                let arr = Array::new_with_length(feature_count_u32(n));\n                for (i, f) in tile.features.iter().enumerate() {\n                    if let Some(PropValue::F64(Some(v))) = f.properties.get(col_idx) {\n                        arr.set(idx_u32(i), JsValue::from_f64(*v));\n                    }\n                }\n                arr.into()\n            } else {\n                let buf: Vec<f64> = tile\n                    .features\n                    .iter()\n                    .filter_map(|f| {\n                        if let Some(PropValue::F64(v)) = f.properties.get(col_idx) {\n                            *v\n                        } else {\n                            None\n                        }\n                    })\n                    .collect();\n                Float64Array::from(buf.as_slice()).into()\n            }\n        }\n        Some(PropValue::Str(_)) | None => {\n            let arr = Array::new_with_length(feature_count_u32(n));\n            for (i, f) in tile.features.iter().enumerate() {\n                if let Some(PropValue::Str(Some(s))) = f.properties.get(col_idx) {\n                    arr.set(idx_u32(i), JsValue::from_str(s));\n                }\n            }\n            arr.into()\n        }\n    }\n}\n\n/// Convert a single [`PropValue`] to a JS primitive for the per-feature\n/// compatibility API.  Returns `None` for absent values.\n#[allow(clippy::cast_precision_loss)]\npub(crate) fn prop_value_to_js(val: &PropValue) -> Option<JsValue> {\n    match val {\n        PropValue::Bool(v) => v.map(JsValue::from_bool),\n        PropValue::I8(v) => v.map(|n| JsValue::from_f64(f64::from(n))),\n        PropValue::U8(v) => v.map(|n| JsValue::from_f64(f64::from(n))),\n        PropValue::I32(v) => v.map(|n| JsValue::from_f64(f64::from(n))),\n        PropValue::U32(v) => v.map(|n| JsValue::from_f64(f64::from(n))),\n        PropValue::I64(v) => v.map(|n| JsValue::from_f64(n as f64)),\n        PropValue::U64(v) => v.map(|n| JsValue::from_f64(n as f64)),\n        PropValue::F32(v) => v.map(|n| JsValue::from_f64(f64::from(n))),\n        PropValue::F64(v) => v.map(JsValue::from_f64),\n        PropValue::Str(v) => v.as_deref().map(JsValue::from_str),\n    }\n}\n"
  },
  {
    "path": "rust/mlt-wasm/src/tile.rs",
    "content": "use js_sys::{Array, Float64Array, Object, Reflect};\nuse wasm_bindgen::prelude::*;\n\nuse crate::geometry::LayerGeometry;\nuse crate::layer::DecodedLayer;\nuse crate::properties::{build_prop_cache, prop_value_to_js};\n\n/// A fully decoded MLT tile.\n///\n/// Construct one via [`crate::decode_tile`], then use the index-based accessors\n/// to read layer metadata and per-feature data.\n///\n/// All decoding is done eagerly at construction time via [`TileLayer`](mlt_core::TileLayer).\n#[wasm_bindgen]\npub struct MltTile {\n    pub(crate) layers: Vec<DecodedLayer>,\n}\n\n#[wasm_bindgen]\nimpl MltTile {\n    // -----------------------------------------------------------------------\n    // Layer metadata\n    // -----------------------------------------------------------------------\n\n    /// Number of layers in this tile.\n    #[must_use]\n    pub fn layer_count(&self) -> usize {\n        self.layers.len()\n    }\n\n    /// Name of layer `layer_idx`.\n    #[must_use]\n    pub fn layer_name(&self, layer_idx: usize) -> String {\n        self.layers[layer_idx].tile.name.clone()\n    }\n\n    /// Extent of layer `layer_idx` in tile coordinates (typically 4096).\n    #[must_use]\n    pub fn layer_extent(&self, layer_idx: usize) -> u32 {\n        self.layers[layer_idx].tile.extent\n    }\n\n    /// Number of features in layer `layer_idx`.\n    #[must_use]\n    pub fn feature_count(&self, layer_idx: usize) -> usize {\n        self.layers[layer_idx].tile.features.len()\n    }\n\n    // -----------------------------------------------------------------------\n    // Bulk typed-array accessors\n    // -----------------------------------------------------------------------\n\n    /// MVT geometry types — collapses single and multi into `1`/`2`/`3`.\n    #[must_use]\n    pub fn layer_types(&self, layer_idx: usize) -> js_sys::Uint8Array {\n        self.layers[layer_idx].types_array.clone()\n    }\n\n    /// Original MLT geometry types — preserves the single vs multi distinction.\n    #[must_use]\n    pub fn layer_mlt_types(&self, layer_idx: usize) -> js_sys::Uint8Array {\n        self.layers[layer_idx].mlt_types_array.clone()\n    }\n\n    /// All feature IDs for layer `layer_idx` as a `Float64Array`.\n    ///\n    /// One `f64` per feature.  Absent IDs are `NaN`.\n    #[must_use]\n    pub fn layer_ids(&self, layer_idx: usize) -> Float64Array {\n        let features = &self.layers[layer_idx].tile.features;\n        let floats: Vec<f64> = features\n            .iter()\n            .map(|f| {\n                #[expect(clippy::cast_precision_loss)]\n                f.id.map_or(f64::NAN, |v| v as f64)\n            })\n            .collect();\n        Float64Array::from(floats.as_slice())\n    }\n\n    /// All decoded geometry arrays for layer `layer_idx`, in one call.\n    #[must_use]\n    pub fn layer_geometry(&self, layer_idx: usize) -> LayerGeometry {\n        LayerGeometry::from_values(&self.layers[layer_idx].geometry)\n    }\n\n    // -----------------------------------------------------------------------\n    // Bulk property API\n    // -----------------------------------------------------------------------\n\n    /// Column names for layer `layer_idx` as a JS `Array` of strings.\n    pub fn layer_property_keys(&self, layer_idx: usize) -> Array {\n        let tile = &self.layers[layer_idx].tile;\n        build_prop_cache(tile).keys\n    }\n\n    /// All property values for layer `layer_idx` as a JS `Array` of columns.\n    pub fn layer_properties(&self, layer_idx: usize) -> Array {\n        let tile = &self.layers[layer_idx].tile;\n        build_prop_cache(tile).columns\n    }\n\n    // -----------------------------------------------------------------------\n    // Compatibility: per-feature API\n    // -----------------------------------------------------------------------\n\n    /// Properties for a single feature as a plain JS object.\n    #[must_use]\n    pub fn feature_properties(&self, layer_idx: usize, feature_idx: usize) -> Object {\n        let tile = &self.layers[layer_idx].tile;\n        let obj = Object::new();\n        if let Some(feature) = tile.features.get(feature_idx) {\n            for (name, val) in tile.property_names.iter().zip(feature.properties.iter()) {\n                if let Some(js_val) = prop_value_to_js(val) {\n                    let _ = Reflect::set(&obj, &JsValue::from_str(name), &js_val);\n                }\n            }\n        }\n        obj\n    }\n}\n"
  },
  {
    "path": "rust/mlt-wasm/tsconfig.json",
    "content": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2022\",\n\t\t\"module\": \"ES2022\",\n\t\t\"moduleResolution\": \"bundler\",\n\t\t\"lib\": [\"ES2022\", \"DOM\"],\n\t\t\"rootDir\": \"js\",\n\t\t\"outDir\": \"dist\",\n\t\t\"declaration\": true,\n\t\t\"declarationMap\": true,\n\t\t\"sourceMap\": true,\n\t\t\"strict\": true,\n\t\t\"skipLibCheck\": true\n\t},\n\t\"include\": [\"js/**/*\"],\n\t\"exclude\": [\"node_modules\", \"dist\", \"pkg\", \"**/*.spec.ts\", \"**/*.bench.ts\"]\n}\n"
  },
  {
    "path": "rust/mlt-wasm/vitest.config.ts",
    "content": "import topLevelAwait from \"vite-plugin-top-level-await\";\nimport wasm from \"vite-plugin-wasm\";\nimport { defineConfig } from \"vitest/config\";\n\nexport default defineConfig({\n  plugins: [wasm(), topLevelAwait()],\n});\n"
  },
  {
    "path": "rust/mod.just",
    "content": "main_crate := 'mlt-core'\njust := quote(just_executable())\n\n# if running in CI, treat warnings as errors by setting RUSTFLAGS and RUSTDOCFLAGS to '-D warnings' unless they are already set\n# Use `CI=true just ci-test` to run the same tests as in GitHub CI.\n# Use `just ci-env-info` to see the current values of RUSTFLAGS and RUSTDOCFLAGS\nci_mode := if env('CI', '') != '' {'1'} else {''}\nexport RUSTFLAGS := env('RUSTFLAGS', if ci_mode == '1' {'-D warnings'} else {''})\nexport RUSTDOCFLAGS := env('RUSTDOCFLAGS', if ci_mode == '1' {'-D warnings'} else {''})\nexport RUST_BACKTRACE := env('RUST_BACKTRACE', if ci_mode == '1' {'1'} else {'0'})\n\n# Features excluded from cargo-hack --each-feature iterations (heavy optional instrumentation)\nhack_skip := '--exclude-features __hotpath,__hotpath-alloc,__hotpath-mcp'\n\n_default: (just '--list' 'rust')\n\n[private]\njust *args:\n    {{just}} {{args}}\n\n# Run integration tests and save its output as the new expected output\nbless *args: (just 'cargo-install' 'cargo-insta') (just 'cargo-install' 'cargo-hack')\n    cargo hack insta test --accept --unreferenced=delete --each-feature {{hack_skip}} \\\n        --package mlt-core --exclude-no-default-features --exclude-all-features \\\n        {{args}}\n\n# Run benchmarks\nbench *args: (just 'cargo-install' 'cargo-hack')\n    cargo hack bench --all-targets --each-feature {{hack_skip}} \\\n        --package mlt-core --exclude-no-default-features --exclude-all-features \\\n        {{args}}\n\n# Run a specific benchmark target\nbench-one target='encoding_from_mvt': (just 'cargo-install' 'cargo-hack')\n    cargo bench --bench {{target}} -p mlt-core --features __private\n\nalias b := build\n# Build the project\nbuild: (just 'cargo-install' 'cargo-hack')\n    cargo hack build --all-targets --each-feature {{hack_skip}} \\\n        --workspace --exclude-no-default-features --exclude-all-features\n\nalias c := check\n# Quick compile without building a binary\ncheck: (just 'cargo-install' 'cargo-hack')\n    cargo hack check --all-targets --each-feature {{hack_skip}} \\\n        --workspace --exclude-no-default-features --exclude-all-features\n    cd mlt-core/fuzz && cargo check --all-features\n\n# Generate code coverage report to upload to codecov.io\nci-coverage: ci-env-info _coverage\n    {{just}} assert-git-is-clean  # call it explicitly to avoid just optimizing it to just one call\n    mkdir -p target/llvm-cov      # the output-path here is used in the CI workflow\n    cargo llvm-cov report --include-build-script --lcov --output-path target/llvm-cov/lcov.info\n\n# Run minimal subset of tests to ensure compatibility with MSRV\nci-check: ci-env-info check\n\n# Print environment info for CI\nci-env-info:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    echo \"::group::General environment Information\"\n    echo \"Running for '{{main_crate}}' crate {{if ci_mode == '1' {'in CI mode'} else {'in dev mode'} }} on {{os()}} / {{arch()}}\"\n    echo \"PWD {{justfile_directory()}}\"\n    {{just}} --version\n    rustc --version\n    cargo --version\n    rustup --version\n    echo \"RUSTFLAGS='$RUSTFLAGS'\"\n    echo \"RUSTDOCFLAGS='$RUSTDOCFLAGS'\"\n    echo \"RUST_BACKTRACE='$RUST_BACKTRACE'\"\n    echo \"::endgroup::\"\n\n    # Linux\n    if command -v lscpu &>/dev/null; then\n        echo \"::group::lscpu\"\n        lscpu\n        echo \"::endgroup::\"\n    fi\n    if [ -f /proc/cpuinfo ]; then\n        echo \"::group::/proc/cpuinfo\"\n        cat /proc/cpuinfo\n        echo \"::endgroup::\"\n    fi\n\n    # MacOS\n    if command -v sysctl &>/dev/null && command -v egrep &>/dev/null; then\n        echo \"::group::sysctl\"\n        sysctl -a 2>/dev/null | egrep \"^hw\" || true # ignore permission denied to some sysctl entries\n        echo \"::endgroup::\"\n    fi\n\n# Run linting as expected by CI\nci-lint: ci-env-info test-fmt clippy ci-docs-rs\n    {{just}} assert-git-is-clean  # call it explicitly to avoid just optimizing it to just one call\n\n# Run all tests as expected by CI\nci-test: ci-env-info (just 'java::gen-snippets') test\n    {{just}} assert-git-is-clean  # call it explicitly to avoid just optimizing it to just one call\n\n# Run all tests as expected by CI for wasm\nci-test-wasm: ci-env-info wasm-test\n    {{just}} assert-git-is-clean  # call it explicitly to avoid just optimizing it to just one call\n\n# Run minimal subset of tests to ensure compatibility with MSRV\nci-test-msrv: ci-env-info\n    #!/usr/bin/env bash\n    set -euo pipefail\n    # For MSRV, relax all warnings to avoid different warnings per rust version\n    unset CI RUSTFLAGS RUSTDOCFLAGS RUST_BACKTRACE\n    # Pre-install cargo-hack to avoid CI binstall issues\n    {{just}} cargo-install cargo-hack\n    {{just}} rust::ci-check\n    {{just}} java::gen-snippets\n    {{just}} rust::test\n    {{just}} assert-git-is-clean\n\n# Build the layer target (installs `cargo fuzz`)\nci-fuzz-build target: (cargo-fuzz 'build' target '--target' 'x86_64-unknown-linux-gnu')\n\n# Fuzz the target for 90 seconds (installs `cargo fuzz`)\nci-fuzz-run target: (cargo-fuzz 'run' target '--target' 'x86_64-unknown-linux-gnu' '--' '-max_total_time=90' '-max_len=300' '-rss_limit_mb=2048')\n\n# Run coverage against a previously generated corpus and export LCOV for Codecov upload.\n# Output: target/llvm-cov/fuzz-<target>.lcov\n[working-directory: 'mlt-core']\nci-fuzz-cov target: (just 'cargo-install' 'cargo-fuzz')\n    #!/usr/bin/env bash\n    set -euo pipefail\n\n    LCOV_OUT=\"../target/llvm-cov/fuzz-{{target}}.lcov\"\n    mkdir -p \"$(dirname \"$LCOV_OUT\")\"\n    {{just}} rust::fuzz-llvm-cov {{target}} \"$LCOV_OUT\" export --format=lcov\n    echo \"Fuzz coverage LCOV: $LCOV_OUT\"\n\n# Build and package release binary for CI\nci-build-release target artifact_name:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    rustup target add {{target}}\n    cargo build --release --target {{target}} --package mlt --bin mlt\n    cd target/{{target}}/release\n    if [ -f mlt.exe ]; then\n        7z a ../../../../{{artifact_name}} mlt.exe\n    else\n        tar -czf ../../../../{{artifact_name}} mlt\n    fi\n\n# Output version and tag for GITHUB_OUTPUT (append with >> $GITHUB_OUTPUT)\nci-get-release-info package='mlt':\n    #!/usr/bin/env bash\n    set -euo pipefail\n    VERSION=$({{just}} rust::get-crate-field version {{package}})\n    echo \"version=$VERSION\"\n    echo \"tag=rust-{{package}}-v$VERSION\"\n\n# Clean all build artifacts\nclean:\n    cargo clean\n    rm -f Cargo.lock\n\n# Run cargo clippy to lint the code\nclippy *args: (just 'cargo-install' 'cargo-hack')\n    cargo clippy --all-targets --all-features --workspace {{args}}\n    cd mlt-core/fuzz && cargo clippy --all-features {{args}}\n\n# Generate code coverage report and open it\ncoverage: _coverage\n    cargo llvm-cov report --include-build-script --open\n\n# Generate code coverage report. Installs `cargo llvm-cov`\n_coverage: (just 'cargo-install' 'cargo-hack') (just 'cargo-install' 'cargo-llvm-cov') (just 'java::gen-snippets')\n    cargo llvm-cov clean --workspace\n    cargo hack llvm-cov --no-report --all-targets --each-feature {{hack_skip}} \\\n        --workspace --exclude-no-default-features --exclude-all-features\n    rm -rf ../test/synthetic/0x01-rust\n    cargo llvm-cov run --no-report -p mlt-synthetics\n    cargo llvm-cov run --no-report -p mlt -- ls --validate-to-json --details basic ../test/synthetic/\n    rustup toolchain install nightly --profile minimal --component llvm-tools\n    cargo +nightly llvm-cov --no-report --doctests --workspace\n\n# Run the encoder with hotpath profiling (timing + allocation tracking)\nprofile *args:\n    cargo run --release -p mlt --features='__hotpath,__hotpath-alloc,__hotpath-mcp' -- {{args}}\n\n# Download ~5k benchmark tiles (z0-z6) from Protomaps into outdir.\n# Skips if a previous complete download is detected (.complete sentinel).\n# ODbL-licensed OSM data; extraction explicitly permitted by Protomaps.\ndownload-benchmark-tiles outdir='/tmp/benchmark-tiles' maxzoom='6': (just 'install-pmtiles')\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if [ -f \"{{outdir}}/.complete\" ]; then\n        echo \"{{outdir}} already has tiles, skipping download\"\n        exit 0\n    fi\n    TMPFILE=$(mktemp /tmp/benchmark-XXXXXX.pmtiles)\n    trap 'rm -f \"$TMPFILE\"' EXIT\n    pmtiles extract \\\n        https://data.source.coop/protomaps/openstreetmap/v4.pmtiles \\\n        \"$TMPFILE\" \\\n        --maxzoom={{maxzoom}}\n    python3 - \"{{outdir}}\" \"{{maxzoom}}\" \"$TMPFILE\" << 'PYEOF'\n    import gzip, os, sys\n    from pmtiles.reader import Reader, MmapSource, Compression\n\n    out, maxzoom, src = sys.argv[1], int(sys.argv[2]), sys.argv[3]\n    os.makedirs(out, exist_ok=True)\n    count = 0\n    with open(src, \"rb\") as f:\n        reader = Reader(MmapSource(f))\n        is_gzip = reader.header()[\"tile_compression\"] == Compression.GZIP\n        for z in range(maxzoom + 1):\n            for x in range(2 ** z):\n                for y in range(2 ** z):\n                    data = reader.get(z, x, y)\n                    if data:\n                        if is_gzip:\n                            data = gzip.decompress(data)\n                        with open(os.path.join(out, f\"{z}_{x}_{y}.mvt\"), \"wb\") as of:\n                            of.write(data)\n                        count += 1\n    print(f\"Extracted {count} tiles to {out}\")\n    PYEOF\n    touch \"{{outdir}}/.complete\"\n\n# Build and open code documentation\ndocs *args='--open':\n    DOCS_RS=1 cargo doc --no-deps {{args}} --features arbitrary --workspace\n\nalias f := fmt\nfmt: (_fmt) (_fmt '--manifest-path' 'mlt-core/fuzz/Cargo.toml')\n\n# Reformat code. If nightly is available, use it for better results.\n_fmt *ARGS:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if (rustup toolchain list | grep nightly && rustup component list --toolchain nightly | grep rustfmt) &> /dev/null; then\n        echo 'Reformatting Rust code using nightly Rust fmt to sort imports'\n        cargo +nightly fmt --all {{ARGS}} -- --config imports_granularity=Module,group_imports=StdExternalCrate\n    else\n        echo 'Reformatting Rust with the stable cargo fmt.  Install nightly with `rustup install nightly` for better results'\n        cargo fmt --all {{ARGS}}\n    fi\n\n# Reformat all Cargo.toml files (installs `cargo sort`)\nfmt-toml *args: (just 'cargo-install' 'cargo-sort')\n    cargo sort --workspace --grouped {{args}}\n\n# Fuzz the layer target (installs `cargo fuzz`)\nfuzz target='layer' *args: (cargo-fuzz 'run' target '--target' 'x86_64-unknown-linux-gnu' '--' args)\n\n# Seed the layer fuzz corpus from all .mlt test files (run once, or after new .mlts are added)\nfuzz-seed target='layer': (just 'cargo-install' 'fd' 'fd-find')\n    mkdir -p mlt-core/fuzz/corpus/{{target}}\n    fd . ../test --extension mlt --threads=1 --exec cp --force {} mlt-core/fuzz/corpus/{{target}}/\n    {{just}} rust::fuzz-cmin {{target}}\n\n# Minimize the fuzz corpus for target (slow — run occasionally to prune redundant entries)\nfuzz-cmin target='layer': (cargo-fuzz 'cmin' target '--target' 'x86_64-unknown-linux-gnu')\n\n# Collect coverage from the fuzz corpus and open an HTML report (installs `cargo fuzz`, `rustfilt`)\n[working-directory: 'mlt-core']\nfuzz-cov target='layer': (just 'cargo-install' 'cargo-fuzz') (just 'cargo-install' 'rustfilt')\n    #!/usr/bin/env bash\n    set -euo pipefail\n\n    HTML_DIR=\"fuzz/coverage/{{target}}/html\"\n    mkdir -p \"$HTML_DIR\"\n    {{just}} rust::fuzz-llvm-cov {{target}} \"\" show \\\n        --format=html \\\n        --output-dir=\"$HTML_DIR\" \\\n        --show-instantiations \\\n        --show-line-counts-or-regions \\\n        --Xdemangler=rustfilt\n    echo \"Coverage report: $HTML_DIR/index.html\"\n    xdg-open \"$HTML_DIR/index.html\" 2>/dev/null || open \"$HTML_DIR/index.html\" 2>/dev/null || true\n\n# Minimize the crash in file (installs `cargo fuzz`)\nfuzz-tmin file target='layer': (cargo-fuzz 'tmin' target file)\n\n# Get any package's field from the metadata\nget-crate-field field package=main_crate: (just 'assert-cmd' 'jq')\n    cargo metadata --format-version 1 | jq -e -r '.packages | map(select(.name == \"{{package}}\")) | first | .{{field}} | select(. != null)'\n\n# Get the minimum supported Rust version (MSRV) for the crate\nget-msrv package=main_crate: (get-crate-field 'rust_version' package)\n\n# Run formatting, cargo check, and clippy for local development\nlint: fmt check clippy test\n\n# Find the minimum supported Rust version and update Cargo.toml\nmsrv: (just 'cargo-install' 'cargo-msrv')\n    cargo msrv find --write-msrv --ignore-lockfile --component rustfmt -- {{just}} rust::ci-test-msrv\n\n# Build mlt-py and launch a Python interpreter with mlt pre-imported\n[working-directory: 'mlt-py']\npy: (just 'cargo-install' 'maturin')\n    #!/usr/bin/env bash\n    set -euo pipefail\n    if [[ ! -d \".venv\" ]]; then\n        echo \"Please make sure to create your virtual environment by running 'python -m venv .venv'\"\n        exit 1\n    fi\n    source .venv/bin/activate\n    maturin develop\n    python3 -i -c \"import maplibre_tiles; print('maplibre_tiles ready — try: maplibre_tiles.decode_mlt(data)')\"\n\npy-publish:\n    docker run --rm -v $(pwd):/io ghcr.io/pyo3/maturin build --release\n\n# Run cargo-release\nrelease *args='': (just 'cargo-install' 'release-plz')\n    release-plz {{args}}\n\n# Check semver compatibility with prior published version\nsemver *args: (just 'cargo-install' 'cargo-semver-checks') (just 'cargo-install' 'cargo-hack')\n    cargo hack semver-checks --each-feature {{hack_skip}} \\\n        --package mlt-core --exclude-no-default-features --exclude-all-features \\\n        {{args}}\n\n# Regenerate the Python type stub for mlt-py\nsync-pyo3-stubs:\n    cargo run -p mlt-py --bin stub_gen --features abi3\n\n# Sync the version field in mlt-py/pyproject.toml to match mlt-py/Cargo.toml\nsync-pyo3-version:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    VERSION=$({{just}} rust::get-crate-field version mlt-py)\n    PYPROJECT=\"mlt-py/pyproject.toml\"\n    sed -i \"s/^version = \\\".*\\\"/version = \\\"$VERSION\\\"/\" \"$PYPROJECT\"\n    echo \"Synced $PYPROJECT to version $VERSION\"\n\n# Sync the version field in mlt-wasm/package.json to match mlt-wasm/Cargo.toml\nsync-wasm-version:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    VERSION=$({{just}} rust::get-crate-field version mlt-wasm)\n    PACKAGE_JSON=\"mlt-wasm/package.json\"\n    jq --arg VERSION \"$VERSION\" '.version = $VERSION' \"$PACKAGE_JSON\" > \"$PACKAGE_JSON.tmp\" && mv \"$PACKAGE_JSON.tmp\" \"$PACKAGE_JSON\"\n    echo \"Synced $PACKAGE_JSON to version $VERSION\"\n    if [ -f \"mlt-wasm/package-lock.json\" ]; then\n        PACKAGE_LOCK=\"mlt-wasm/package-lock.json\"\n        jq --arg VERSION \"$VERSION\" '.version = $VERSION | .packages[\"\"].version = $VERSION' \"$PACKAGE_LOCK\" > \"$PACKAGE_LOCK.tmp\" && mv \"$PACKAGE_LOCK.tmp\" \"$PACKAGE_LOCK\"\n        echo \"Synced $PACKAGE_LOCK to version $VERSION\"\n    fi\n\nalias t := test\n# Run all tests\ntest: (just 'cargo-install' 'cargo-hack')\n    cargo hack test --all-targets --each-feature {{hack_skip}} \\\n        --workspace --exclude-no-default-features --exclude-all-features\n    {{just}} rust::generate-synthetic-mlts\n    {{just}} rust::test-doc\n\n# Test documentation generation\ntest-doc: (docs '--document-private-items')\n    cargo test --doc --features arbitrary --package mlt-core\n\n# Simulate docs.rs for all workspace crates and verify docs can build offline.\nci-docs-rs:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    rustup toolchain install nightly --profile minimal\n    export DOCS_RS=1\n    export RUSTDOCFLAGS=\"${RUSTDOCFLAGS:+$RUSTDOCFLAGS } --cfg docsrs\"\n    cargo fetch --locked\n    CARGO_NET_OFFLINE=true cargo +nightly doc --workspace --no-deps --locked\n\n# Test code formatting\ntest-fmt: (fmt-toml '--check' '--check-format')\n    cargo fmt --all -- --check\n\n# Test workspace publishing with --dry-run\ntest-publish:\n    cargo publish --workspace --dry-run\n\n# Find unused dependencies\nudeps: (just 'cargo-install' 'cargo-udeps')\n    cargo +nightly udeps --all-targets --workspace --all-features\n\n# Update all dependencies, including breaking changes\nupdate:\n    cargo +nightly -Z unstable-options update --breaking\n    cargo update\n    cd mlt-core/fuzz && cargo +nightly -Z unstable-options update --breaking\n    cd mlt-core/fuzz && cargo update\n\n# Generate synthetic .mlt files and ensure there are no duplicates\ngenerate-synthetic-mlts *args:\n    #!/usr/bin/env bash\n    set -euo pipefail\n    rm -rf ../test/synthetic/0x01-rust\n    cargo run -p mlt-synthetics -- {{args}}\n    {{just}} rust::validate-synthetic\n    {{just}} _assert-all-mlt-files-different\n\n# Create .dump files for all .mlt files in test/synthetic for all languages\ngenerate-synthetic-dumps: (just 'cargo-install' 'fd' 'fd-find')\n    cargo build --bin mlt\n    fd . ../test/synthetic -e mlt -x sh -c 'target/debug/mlt dump {} > {.}.dump || echo \"Failed to dump {}\"'\n\n# Run mlt-wasm benches\n[working-directory: 'mlt-wasm']\nwasm-bench: wasm-build\n    npm run bench\n\n# Build the mlt-wasm package (compiles Rust to WASM and TypeScript)\n[working-directory: 'mlt-wasm']\nwasm-build: (just 'cargo-install' 'wasm-pack')\n    npm ci --no-fund --no-audit\n    npm run build\n\n# Run mlt-wasm tests\n[working-directory: 'mlt-wasm']\nwasm-test: wasm-build\n    npm run test\n\n# Validate every .mlt in test/synthetic against corresponding .json (GeoJSON)\nvalidate-synthetic:\n    {{just}} mlt ls --validate-to-json --details basic ../test/synthetic/\n\n[private]\n[working-directory: 'mlt-core']\ncargo-fuzz *args:  (just 'cargo-install' 'cargo-fuzz')\n    cargo +nightly fuzz {{args}}\n\n# Resolve llvm-cov from the nightly sysroot and run it against the fuzz profdata for target.\n# Pass output_file=\"\" to write to stdout, or a path to redirect stdout there.\n[private]\n[working-directory: 'mlt-core']\nfuzz-llvm-cov target output_file *args:\n    #!/usr/bin/env bash\n    set -euo pipefail\n\n    rustup component add llvm-tools-preview --toolchain nightly\n    cargo +nightly fuzz coverage {{target}} --target x86_64-unknown-linux-gnu\n\n    SYSROOT=$(rustup run nightly rustc --print sysroot)\n    HOST=$(rustup run nightly rustc -vV | sed -n 's/^host: //p')\n    LLVM_COV=\"$SYSROOT/lib/rustlib/$HOST/bin/llvm-cov\"\n    FUZZ_DIR=\"fuzz\"\n    TARGET_BIN=$(find target -type f -name '{{target}}' -path '*/coverage/*/release/{{target}}' 2>/dev/null | head -1)\n    echo \"target_bin: $TARGET_BIN\" >&2\n    PROFDATA=\"$FUZZ_DIR/coverage/{{target}}/coverage.profdata\"\n    [ -n \"{{output_file}}\" ] && exec > \"{{output_file}}\"\n    \"$LLVM_COV\" {{args}} \"$TARGET_BIN\" \\\n        --instr-profile=\"$PROFDATA\" \\\n        --ignore-filename-regex=\"(/.cargo/registry|/rustup/toolchains)\"\n"
  },
  {
    "path": "rust/tomlfmt.toml",
    "content": "table_order = [\n    'workspace',\n    'package',\n    'lib',\n    'bin',\n    'example',\n    'bench',\n    'features',\n    'dependencies',\n    'build-dependencies',\n    'dev-dependencies',\n    'profile',\n    'lints',\n]\n"
  },
  {
    "path": "spec/schema/mlt_tileset_metadata.proto",
    "content": "syntax = \"proto3\";\n\npackage mlt;\n\noption java_package = \"org.maplibre.mlt.metadata.tileset\";\n\nmessage TileSetMetadata {\n  int32 version = 1;\n  repeated FeatureTableSchema featureTables = 2;\n  optional string name = 3;\n  optional string description = 4;\n  optional string attribution = 5;\n  optional int32 minZoom = 6;\n  optional int32 maxZoom = 7;\n  // order left, bottom, right, top in WGS84\n  repeated double bounds = 8;\n  // order longitude, latitude in WGS84\n  repeated double center = 9;\n}\n\nmessage FeatureTableSchema {\n  string name = 1;\n  repeated Column columns = 2;\n}\n\n// Column are top-level types in the schema\nmessage Column {\n  string name = 1;\n  // specifies if the values are optional in the column and a present stream should be used\n  bool nullable = 2;\n  ColumnScope columnScope = 3;\n  oneof type {\n    ScalarColumn scalarType = 4;\n    ComplexColumn complexType = 5;\n  }\n}\n\nmessage ScalarColumn {\n  // this belongs elsewhere, but is here for now\n  bool longID = 1;\n  oneof type {\n    ScalarType physicalType = 4;\n    LogicalScalarType logicalType = 5;\n  }\n}\n\n// The type tree is flattened in to a list via a pre-order traversal\n// Represents a column if it is a root (top-level) type or a child of a nested type\nmessage ComplexColumn {\n  oneof type {\n    ComplexType physicalType = 4;\n    LogicalComplexType logicalType = 5;\n  }\n  // The complex type Geometry and the logical type BINARY have no children since there layout is implicit known.\n  // RangeMap has only one child specifying the type of the value since the key is always a vec2<double>.\n  repeated Field children = 6;\n}\n\n// Fields define nested or leaf types in the schema as part of a complex type definition\nmessage Field {\n  // name and nullable are only needed in combination with a struct not for vec, list and map\n  // Map -> has the order key type, value type\n  optional string name = 1;\n  optional bool nullable = 2;\n  oneof type {\n    ScalarField scalarField = 3;\n    ComplexField complexField = 4;\n  }\n}\n\nmessage ScalarField {\n  oneof type {\n    ScalarType physicalType = 1;\n    LogicalScalarType logicalType = 2;\n  }\n}\n\nmessage ComplexField {\n  oneof type {\n    ComplexType physicalType = 1;\n    LogicalComplexType logicalType = 2;\n  }\n  repeated Field children = 3;\n}\n\nenum ColumnScope {\n  // 1:1 Mapping of property and feature -> id and geometry\n  FEATURE = 0;\n  // For M-Values -> 1:1 Mapping for property and vertex\n  VERTEX = 1;\n}\n\nenum ScalarType {\n  BOOLEAN = 0;\n  INT_8 = 1;\n  UINT_8 = 2;\n  INT_32 = 3;\n  UINT_32 = 4;\n  INT_64 = 5;\n  UINT_64 = 6;\n  FLOAT = 7;\n  DOUBLE = 8;\n  STRING = 9;\n}\n\nenum ComplexType {\n  GEOMETRY = 0;\n  STRUCT = 1;\n}\n\nenum LogicalScalarType {\n  ID = 0;\n}\n\nenum LogicalComplexType {\n  // physical type: list<UInt8>\n  BINARY = 0;\n  // physical type: map<vec2<double, T>> -> special data structure which can be used for a efficient representation of linear referencing\n  RANGE_MAP = 1;\n}\n"
  },
  {
    "path": "test/.gitignore",
    "content": "output/\n\n# Ignore generated stream files.\nexpected/*/*/*.bin\nexpected/*/*/*.json\n\n# The per-tile directories also need to be hidden\nexpected/*/*/*/*\n\n# Dump files\n**/*.dump\n"
  },
  {
    "path": "test/.java-version",
    "content": "24\n"
  },
  {
    "path": "test/convert_tiles",
    "content": "#!/bin/bash\n\n# terminate when subshell is killed\ntrap 'trap \" \" SIGINT SIGTERM SIGHUP; kill 0; exit 1' SIGINT SIGTERM SIGHUP\n\n# Build a metadata-and-tile file by concatenating the metadata size, metadata, and tile data\nbundle_meta () {\n    TILE_FILE=\"$1\"\n    META_FILE=\"$2\"\n    OUT_FILE=\"$3\"\n    printf \"%.8x\\n\" $(stat --format=%s \"$META_FILE\") | \\\n        sed -E 's/(..)(..)(..)(..)/0: \\4\\3\\2\\1/' | \\\n        xxd -r -l 4 -g 4 | \\\n        cat - \"$META_FILE\" \"$TILE_FILE\" >\"$OUT_FILE\"\n}\n\nconvert_tile () {\n    z=$1\n    x=$2\n    y=$3\n    SOURCE_PATH=\"$4\"\n    TARGET_PATH=\"$5\"\n\n    MVT_FILE=${z}_${x}_${y}.mvt\n    TILE_PATH=\"$TARGET_PATH/$z/$x\"\n    mkdir -p \"$TILE_PATH\"\n\n    if [ ! -f \"$TARGET_PATH/$z/$x/$y.mlt\" ] || [ ! -f \"$TARGET_PATH/$z/$x/$y.mlt.meta.pbf\" ]; then\n        rm -f \"$TILE_PATH/$y.mltm\"\n        # Write MLT and metadata to target location\n        (cd ../java ; $ENCODE -mvt \"$SOURCE_PATH/$MVT_FILE\" -mlt \"$TARGET_PATH/$z/$x/$y.mlt\" -metadata $6 $7 $8)\n        RESULT=$?\n        if [ $RESULT -ne 0 ] ; then\n            echo \"  $SOURCE_PATH/$MVT_FILE Failed: $RESULT\"\n            rm -f \"$TILE_PATH/$y.mlt\"*\n            return $RESULT\n        fi\n    fi\n\n    if [ ! -f \"$TILE_PATH/$y.mltm\" ]; then\n        # Combine MLT and metadata\n        bundle_meta \"$TILE_PATH/$y.mlt\" \"$TILE_PATH/$y.mlt.meta.pbf\" \"$TILE_PATH/$y.mltm\"\n    fi\n\n    return 0\n}\n\nif [ -f ignore_list_basic ]; then\n    IFS=$'\\r\\n' command eval 'IGNORE_LIST_BASIC=($(cat ignore_list_basic))'\nfi\nif [ -f ignore_list_advanced ]; then\n    IFS=$'\\r\\n' command eval 'IGNORE_LIST_ADVANCED=($(cat ignore_list_advanced))'\nfi\nisIgnoreBasic () {\n    [[ \" ${IGNORE_LIST_BASIC[@]} \" =~ \" $1 \" ]] && return 0 || return 1\n}\nisIgnoreAdvanced () {\n    [[ \" ${IGNORE_LIST_ADVANCED[@]} \" =~ \" $1 \" ]] && return 0 || return 1\n}\n\n# Find OMT tile files\nFILES=$(cd fixtures/omt ; ls -1 *.mvt)\n\nENCODE=\"java -jar build/libs/encode.jar\"\n\n# Relative to root\nSRC_PATH=test/fixtures/omt\nBASIC_PATH=test/expected/omt/basic\nADVANCED_PATH=test/expected/omt/advanced\nMVT_PATH=test/expected/omt/mvt\n\necho -r \\\\nProcessing tiles ...\nfor file in $FILES ; do\n    read z x y <<< $(echo $file | sed -E 's/([0-9]+)_([0-9]+)_([0-9]+)\\.mvt/\\1 \\2 \\3/')\n\n    echo \"  $z:$x,$y ...\"\n    MVT_FILE=${z}_${x}_${y}.mvt\n    mkdir -p \"../$BASIC_PATH/$z/$x\" \"../$ADVANCED_PATH/$z/$x\" \"../$MVT_PATH/$z/$x\"\n\n    if ! isIgnoreBasic \"$z/$x/$y\"; then\n        convert_tile $z $x $y \"../$SRC_PATH\" \"../$BASIC_PATH\"\n        if [ $? -ne 0 ]; then\n            echo $z/$x/$y >> ignore_list_basic\n        fi\n    fi\n    if ! isIgnoreAdvanced \"$z/$x/$y\"; then\n        convert_tile $z $x $y \"../$SRC_PATH\" \"../$ADVANCED_PATH\" -advanced\n        if [ $? -ne 0 ]; then\n            echo $z/$x/$y >> ignore_list_advanced\n        fi\n    fi\n    cp \"../$SRC_PATH/$file\" \"../$MVT_PATH/$z/$x/$y.pbf\"\ndone\n\necho -e \\\\nBuilding metadata ...\n\ncat >\"../$BASIC_PATH/metadata.json\" << EOF\n{\n  \"name\": \"omt-basic\",\n  \"type\": \"baselayer\",\n  \"format\": \"mlt\",\n  \"description\": \"description\",\n  \"version\": \"1.0\",\n  \"formatter\": null,\n  \"bounds\": \"-179.9999999749438,-69.99999999526695,179.9999999749438,84.99999999782301\"\n}\nEOF\n\ncat >\"../$ADVANCED_PATH/metadata.json\" << EOF\n{\n  \"name\": \"omt-advanced\",\n  \"type\": \"baselayer\",\n  \"format\": \"mlt\",\n  \"description\": \"description\",\n  \"version\": \"1.0\",\n  \"formatter\": null,\n  \"bounds\": \"-179.9999999749438,-69.99999999526695,179.9999999749438,84.99999999782301\"\n}\nEOF\n\ncat >\"../$MVT_PATH/metadata.json\" << EOF\n{\n  \"name\": \"omt-ref\",\n  \"type\": \"baselayer\",\n  \"format\": \"pbf\",\n  \"description\": \"description\",\n  \"version\": \"1.0\",\n  \"formatter\": null,\n  \"bounds\": \"-179.9999999749438,-69.99999999526695,179.9999999749438,84.99999999782301\"\n}\nEOF\n\nrm -f omt-basic-mlt.mbtiles omt-advanced-mlt.mbtiles omt-ref.mbtiles\n\necho -e \\\\nBuilding omt-basic-mlt.mbtiles ...\nmb-util --image_format=mltm --scheme=tms \"../$BASIC_PATH\" omt-basic-mlt.mbtiles\n\necho -e \\\\nBuilding omt-advanced-mlt.mbtiles ...\nmb-util --image_format=mltm --scheme=tms \"../$ADVANCED_PATH\" omt-advanced-mlt.mbtiles\n\necho -e \\\\nBuilding omt-ref.mbtiles ...\nmb-util --image_format=pbf --scheme=tms \"../$MVT_PATH\" omt-ref.mbtiles\n"
  },
  {
    "path": "test/expected/tag0x01/simple/LICENSE",
    "content": "These test fixtures have the same license as the rest of the documentation and content of this repo: CC0 1.0 Universal\n\nThey were hand crafted by Dane Springmeyer, June 15, 2024\n"
  },
  {
    "path": "test/expected/tag0x01/simple/line-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"LineString\",\n            \"coordinates\": [\n              [\n                -171.56112670898438,\n                83.6767918001807\n              ],\n              [\n                -171.56112670898438,\n                83.67618677120873\n              ],\n              [\n                -171.55563354492188,\n                83.67618677120873\n              ]\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/expected/tag0x01/simple/multiline-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"MultiLineString\",\n            \"coordinates\": [\n              [\n                [\n                  -171.56112670898438,\n                  83.6767918001807\n                ],\n                [\n                  -171.56112670898438,\n                  83.67618677120873\n                ],\n                [\n                  -171.55563354492188,\n                  83.67618677120873\n                ]\n              ],\n              [\n                [\n                  -171.5618133544922,\n                  83.67686742474851\n                ],\n                [\n                  -171.56044006347656,\n                  83.67656492107244\n                ]\n              ]\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/expected/tag0x01/simple/multipoint-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"MultiPoint\",\n            \"coordinates\": [\n              [\n                -171.55906677246094,\n                83.67641366382949\n              ],\n              [\n                -171.56044006347656,\n                83.6767918001807\n              ]\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/expected/tag0x01/simple/multipolygon-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"MultiPolygon\",\n            \"coordinates\": [\n              [\n                [\n                  [\n                    -171.5625,\n                    83.67694304841552\n                  ],\n                  [\n                    -171.55563354492188,\n                    83.67694304841552\n                  ],\n                  [\n                    -171.55563354492188,\n                    83.67618677120873\n                  ],\n                  [\n                    -171.5625,\n                    83.67618677120873\n                  ],\n                  [\n                    -171.5625,\n                    83.67694304841552\n                  ]\n                ]\n              ],\n              [\n                [\n                  [\n                    -171.55494689941406,\n                    83.6761111385334\n                  ],\n                  [\n                    -171.54876708984375,\n                    83.6761111385334\n                  ],\n                  [\n                    -171.54876708984375,\n                    83.67543040391408\n                  ],\n                  [\n                    -171.55494689941406,\n                    83.67543040391408\n                  ],\n                  [\n                    -171.55494689941406,\n                    83.6761111385334\n                  ]\n                ],\n                [\n                  [\n                    -171.55357360839844,\n                    83.67595987048006\n                  ],\n                  [\n                    -171.55357360839844,\n                    83.67565732356232\n                  ],\n                  [\n                    -171.5508270263672,\n                    83.67565732356232\n                  ],\n                  [\n                    -171.5508270263672,\n                    83.67595987048006\n                  ],\n                  [\n                    -171.55357360839844,\n                    83.67595987048006\n                  ]\n                ]\n              ]\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/expected/tag0x01/simple/point-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"Point\",\n            \"coordinates\": [\n              -171.5453338623047,\n              83.67565732356232\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/expected/tag0x01/simple/polygon-boolean.mlt.geojson",
    "content": "{\n  \"layers\": [\n    {\n      \"name\": \"layer\",\n      \"version\": 1,\n      \"extent\": 4096,\n      \"features\": [\n        {\n          \"type\": \"Feature\",\n          \"id\": 1,\n          \"geometry\": {\n            \"type\": \"Polygon\",\n            \"coordinates\": [\n              [\n                [\n                  -171.56044006347656,\n                  83.67648929290141\n                ],\n                [\n                  -171.5570068359375,\n                  83.67603550495716\n                ],\n                [\n                  -171.54876708984375,\n                  83.6743713383338\n                ],\n                [\n                  -171.56044006347656,\n                  83.67648929290141\n                ]\n              ]\n            ]\n          },\n          \"properties\": {\n            \"key\": true\n          }\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "test/fixtures/fastpfor/README.md",
    "content": "# FastPFOR fixtures\n\nThis directory contains cross-language **FastPFOR** test vectors extracted from the C++ tests.\n\n## Origin\n\nThe files correspond to the `compressed{1..4}` / `uncompressed{1..4}` arrays in:\n\n- `cpp/test/test_fastpfor.cpp`\n\n## File formats\n\nFor each `vectorN`:\n\n- `vectorN_encoded.bin`\n  - Big-endian bytes of the FastPFOR-encoded **32-bit word stream** (`uint32_t[]` in C++).\n  - This is the FastPFOR *wire format* as consumed by TS `decodeFastPfor` (big-endian int32 words).\n  - Fixtures are stored in canonical form: trailing `0x00000000` padding words are trimmed.\n- `vectorN_decoded.bin`\n  - Big-endian bytes of the expected **decoded int32 values** (`uint32_t[]` in C++).\n  - When interpreted as signed int32, values use two’s complement (e.g. the `-100..99` range in `vector3`).\n"
  },
  {
    "path": "test/fixtures/omt-planet-20260112.mvt.max1.pmtiles.txt",
    "content": "Downloaded from https://tile.openstreetmap.jp/static/planet-20260126.pmtiles\nSubset with `pmtiles extract planet-20260112.pmtiles omt-planet-20260112.mvt.max1.pmtiles --maxzoom=1`\n"
  },
  {
    "path": "test/fixtures/simple/LICENSE",
    "content": "These test fixtures have the same license as the rest of the documentation and content of this repo: CC0 1.0 Universal\n\nThey were hand crafted by Dane Springmeyer, June 15, 2024\n"
  },
  {
    "path": "test/synthetic/0x01/extent_1073741824.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 1073741824,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            1073741823,\n            1073741823\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_131072.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 131072,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            131071,\n            131071\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_4096.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 4096,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            4095,\n            4095\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_512.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 512,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            511,\n            511\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_1073741824.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 1073741824,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -42,\n            -42\n          ],\n          [\n            1073741866,\n            1073741866\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_1073741824.mlt",
    "content": "*\u0001\u0006layer1\u0004\u0001\u0004\u00030\u0002\u0001\u0001\u00012\u0002\u0001\u0001\u0002\u0013B\u0004\fSS\b\b"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_131072.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 131072,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -42,\n            -42\n          ],\n          [\n            131114,\n            131114\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_131072.mlt",
    "content": "$\u0001\u0006layer1\b\u0001\u0004\u00030\u0002\u0001\u0001\u00012\u0002\u0001\u0001\u0002\u0013B\u0004\bSS\u0010\u0010"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_4096.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 4096,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -42,\n            -42\n          ],\n          [\n            4138,\n            4138\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_4096.mlt",
    "content": "!\u0001\u0006layer1 \u0001\u0004\u00030\u0002\u0001\u0001\u00012\u0002\u0001\u0001\u0002\u0013B\u0004\u0006SSAA"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_512.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 512,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            -42,\n            -42\n          ],\n          [\n            554,\n            554\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/extent_buf_512.mlt",
    "content": "!\u0001\u0006layer1\u0004\u0001\u0004\u00030\u0002\u0001\u0001\u00012\u0002\u0001\u0001\u0002\u0013B\u0004\u0006SS\t\t"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_1.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"a\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_2.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_3.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_4.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_5.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_6.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_7.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/fpf_align_8.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"aaaaaaaa\",\n        \"v\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/id.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 100,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/id64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/id_min.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 0,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64_opt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 101,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 105,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 106,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64_opt_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 101,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 105,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 106,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids64_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 9234567890,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids_opt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 100,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 101,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 105,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 106,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids_opt_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 100,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 101,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 105,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 106,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/ids_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            11,\n            52\n          ],\n          [\n            71,\n            72\n          ],\n          [\n            61,\n            22\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/line.mlt",
    "content": " \u0001\u0006layer1P\u0001\u0004\u00030\u0002\u0001\u0001\u00012\u0002\u0001\u0001\u0003\u0013B\u0006\u0006\u0016hx(\u0013c"
  },
  {
    "path": "test/synthetic/0x01/line_morton_curve_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            8,\n            0\n          ],\n          [\n            0,\n            8\n          ],\n          [\n            8,\n            8\n          ],\n          [\n            16,\n            0\n          ],\n          [\n            24,\n            0\n          ],\n          [\n            16,\n            8\n          ],\n          [\n            24,\n            8\n          ],\n          [\n            0,\n            16\n          ],\n          [\n            8,\n            16\n          ],\n          [\n            0,\n            24\n          ],\n          [\n            8,\n            24\n          ],\n          [\n            16,\n            16\n          ],\n          [\n            24,\n            16\n          ],\n          [\n            16,\n            24\n          ],\n          [\n            24,\n            24\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/line_morton_curve_no_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            8,\n            0\n          ],\n          [\n            0,\n            8\n          ],\n          [\n            8,\n            8\n          ],\n          [\n            16,\n            0\n          ],\n          [\n            24,\n            0\n          ],\n          [\n            16,\n            8\n          ],\n          [\n            24,\n            8\n          ],\n          [\n            0,\n            16\n          ],\n          [\n            8,\n            16\n          ],\n          [\n            0,\n            24\n          ],\n          [\n            8,\n            24\n          ],\n          [\n            16,\n            16\n          ],\n          [\n            24,\n            16\n          ],\n          [\n            16,\n            24\n          ],\n          [\n            24,\n            24\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/line_zero_length.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            6,\n            6\n          ],\n          [\n            6,\n            6\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_line.mlt",
    "content": "(\u0001\u0006layer1P\u0001\u0004\u00030\u0002\u0002\u0002\u0001\u00012\u0002\u0002\u0002\u0003\u0003\u0013B\f\f\nL\u000e\u000e\u00052\u0007?\u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mline.mlt",
    "content": "2\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0001\u00041\u0002\u0001\u0001\u00022\u0002\u0003\u0003\u0003\u0002\u0003\u0013B\u0010\u0010\nL\u000e\u000e\u00052\u001ew$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mpoly.mlt",
    "content": "A\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0001\u00051\u0002\u0001\u0001\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0018\nL\u000e\u000e\u00052\u0003c\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_mpt.mlt",
    "content": ",\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0001\u00031\u0002\u0001\u0001\u00032\u0002\u0001\u0001\u0003\u0013B\f\f\nL\u000e\u000e\u00052\u0005Y\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_poly.mlt",
    "content": ".\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0001\u00022\u0002\u0001\u0001\u00013\u0002\u0002\u0002\u0003\u0003\u0013B\f\r\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_line_polyh.mlt",
    "content": "4\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0001\u00022\u0002\u0001\u0001\u00023\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0012\u0012\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mline_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mline_mline.mlt",
    "content": "8\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0004\u00041\u0002\u0002\u0002\u0002\u00022\u0002\u0004\u0004\u0002\u0003\u0002\u0003\u0013B\u0014\u00140\u0014$\u0010\u0017$$ \u0019\u0014\u0015g$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mline_mpoly.mlt",
    "content": "G\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0004\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0005\u0005\u0002\u0003\u0003\u0003\u0003\u0013B\u001c\u001c0\u0014$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpoly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpoly_mpoly.mlt",
    "content": "S\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0005\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0004\u0004\u0002\u0001\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B$%\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003\u0001W\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpoly_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mline.mlt",
    "content": "2\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0002\u0002\u0002\u0003\u0013B\u0010\u0010\f2\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mpoly.mlt",
    "content": "A\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0018\u0018\f2\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_mpt_mpt.mlt",
    "content": "(\u0001\u0006layer1P\u0001\u0004\u00030\u0002\u0002\u0002\u0003\u00031\u0002\u0002\u0002\u0003\u0003\u0013B\f\f\f2\u001e \u00048!W\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mline.mlt",
    "content": "7\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0002\u00041\u0002\u0001\u0001\u00022\u0002\u0001\u0001\u00013\u0002\u0003\u0003\u0003\u0002\u0003\u0013B\u0010\u0010n\n\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mpoly.mlt",
    "content": "C\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0019n\n\u0006.\"\u000b\u0001\u0003\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_mpt.mlt",
    "content": "2\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0001\u0001\u00013\u0002\u0001\u0001\u0003\u0013B\f\rn\n\u0006.\"\u000b\u0001\u0006\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_poly.mlt",
    "content": ".\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0002\u00022\u0002\u0002\u0002\u0001\u00013\u0002\u0002\u0002\u0003\u0003\u0013B\f\fn\n\u0006.\"\u000b'!\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_poly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_polyh.mlt",
    "content": "5\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0002\u00022\u0002\u0002\u0002\u0001\u00023\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0012\u0012n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_2_poly_polyh_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mpoly.mlt",
    "content": "I\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001ehFK(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_mpt.mlt",
    "content": "8\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0002\u0002\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0001\u0001\u00023\u0002\u0002\u0002\u0003\u0003\u0013B\u0012\u0012hFK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_polyh.mlt",
    "content": "<\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0002\u0002\u0002\u00022\u0002\u0002\u0002\u0002\u00023\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0018hFK(\\\"7+\b\u0014\u0017\u000b8%K(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_2_polyh_polyh_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_2_pt_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mline_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mline_line.mlt",
    "content": ":\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0004\u00011\u0002\u0001\u0001\u00022\u0002\u0004\u0004\u0003\u0002\u0003\u0003\u0013B\u0016\u0016\nL\u000e\u000e\u00052\u001ew$\u0010\u0017$$ \u0019\u0014;/\u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mline_mpoly.mlt",
    "content": "O\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0004\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0002\u0003\u0003\u0003\u0003\u0013B\"\"\nL\u000e\u000e\u00052\u001ew$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpoly_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpoly_line.mlt",
    "content": "J\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0005\u00011\u0002\u0001\u0001\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001f\nL\u000e\u000e\u00052\u0003c\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003\u00013\u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_line.mlt",
    "content": "4\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0003\u00011\u0002\u0001\u0001\u00032\u0002\u0002\u0002\u0003\u0003\u0013B\u0012\u0012\nL\u000e\u000e\u00052\u0005Y\u001e \u00048#=\u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_mline.mlt",
    "content": ":\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0003\u0003\u0003\u0002\u0003\u0013B\u0016\u0016\nL\u000e\u000e\u00052\u0005Y\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_mpt_mpoly.mlt",
    "content": "I\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u001e\u001e\nL\u000e\u000e\u00052\u0005Y\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_line.mlt",
    "content": "7\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0002\u00012\u0002\u0001\u0001\u00013\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0012\u0014\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001 \u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mline.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0002\u00041\u0002\u0001\u0001\u00022\u0002\u0001\u0001\u00013\u0002\u0004\u0004\u0003\u0003\u0002\u0003\u0013B\u0016\u0017\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mpoly.mlt",
    "content": "L\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e \nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001\u0003\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_mpt.mlt",
    "content": ";\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0001\u0001\u00013\u0002\u0002\u0002\u0003\u0003\u0013B\u0012\u0014\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001\u0006\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_poly_polyh.mlt",
    "content": ">\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0002\u00022\u0002\u0002\u0002\u0001\u00023\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0019\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_line.mlt",
    "content": "<\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0001\u0002\u00012\u0002\u0001\u0001\u00023\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0018\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b%\u001f\u000e\u000e\u00052"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_mpoly.mlt",
    "content": "Q\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B$$\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_polyh_mpt.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0001\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0001\u0001\u00023\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0018\u0018\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_line_pt_line.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_line_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_line_mline.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0004\u0001\u00041\u0002\u0002\u0002\u0002\u00022\u0002\u0005\u0005\u0002\u0003\u0003\u0002\u0003\u0013B\u001a\u001a0\u0014$\u0010\u0017$$ \u0019\u0014;/\u000e\u000e\u00052\u001ew$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_mpoly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_mpoly_mline.mlt",
    "content": "U\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0004\u0005\u00041\u0002\u0003\u0003\u0002\u0002\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0007\u0007\u0002\u0003\u0003\u0003\u0003\u0002\u0003\u0013B&&0\u0014$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003ak$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_mpt_mline.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0004\u0003\u00041\u0002\u0003\u0003\u0002\u0003\u00022\u0002\u0004\u0004\u0002\u0003\u0002\u0003\u0013B\u001a\u001a0\u0014$\u0010\u0017$$ \u0019\u00149I\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_poly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_poly_mline.mlt",
    "content": "E\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0004\u0002\u00041\u0002\u0002\u0002\u0002\u00022\u0002\u0001\u0001\u00013\u0002\u0005\u0005\u0002\u0003\u0003\u0002\u0003\u0013B\u001a\u001a0\u0014$\u0010\u0017$$ \u0019\u0014(q\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mline_pt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_line_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_line_mpoly.mlt",
    "content": "[\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0005\u0001\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0004\u0004\u0002\u0001\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B*+\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003\u00013\u000e\u000e\u00052\u0003c\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_mline_mpoly.mlt",
    "content": "`\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0005\u0004\u00051\u0002\u0003\u0003\u0002\u0002\u00022\u0002\u0004\u0004\u0002\u0001\u0002\u00013\u0002\b\b\u0003\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B..\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003ak$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_mpt_mpoly.mlt",
    "content": "[\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0005\u0003\u00051\u0002\u0003\u0003\u0002\u0003\u00022\u0002\u0004\u0004\u0002\u0001\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B*+\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003\u0001M\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_poly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_poly_mpoly.mlt",
    "content": "\\\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0005\u0002\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0005\u0005\u0002\u0001\u0001\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B*+\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003#u\u0006.\"\u000b\u0001\u0003\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_poly_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_polyh_mpoly.mlt",
    "content": "b\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0005\u0002\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0005\u0005\u0002\u0001\u0002\u0002\u00013\u0002\b\b\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B00\u000e(\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003)9K(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_polyh_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpoly_pt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_line_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_line_mpt.mlt",
    "content": "4\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0003\u0001\u00031\u0002\u0002\u0002\u0003\u00032\u0002\u0001\u0001\u0003\u0013B\u0012\u0012\f2\u001e \u00048#=\u000e\u000e\u00052\u0005Y\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mline_mpoly.mlt",
    "content": "O\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0005\u0005\u0002\u0003\u0003\u0003\u0003\u0013B\"\"\f2\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mline_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mline_mpt.mlt",
    "content": ":\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0003\u0004\u00031\u0002\u0003\u0003\u0003\u0002\u00032\u0002\u0002\u0002\u0002\u0003\u0013B\u0016\u0016\f2\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00149I\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mpoly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_mpoly_mpt.mlt",
    "content": "J\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0003\u0005\u00031\u0002\u0003\u0003\u0003\u0002\u00032\u0002\u0002\u0002\u0002\u00013\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001f\f2\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003\u0001M\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_poly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_poly_mpt.mlt",
    "content": ":\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0003\u0002\u00031\u0002\u0002\u0002\u0003\u00032\u0002\u0001\u0001\u00013\u0002\u0001\u0001\u0003\u0013B\u0012\u0013\f2\u001e \u00048@\u0006.\"\u000b\u0001\u0006\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_polyh_mpt.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0003\u0002\u00031\u0002\u0002\u0002\u0003\u00032\u0002\u0001\u0001\u00023\u0002\u0002\u0002\u0003\u0003\u0013B\u0018\u0018\f2\u001e \u00048:CK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_mpt_pt_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_line_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_line_poly.mlt",
    "content": "8\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0002\u0001\u00022\u0002\u0002\u0002\u0001\u00013\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0012\u0014n\n\u0006.\"\u000b\u0001 \u000e\u000e\u00052\\\u0001\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mline_mpoly.mlt",
    "content": "P\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0004\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0006\u0006\u0003\u0002\u0003\u0003\u0003\u0003\u0013B\"\"n\n\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mline_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mline_poly.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0004\u00021\u0002\u0001\u0001\u00022\u0002\u0002\u0002\u0001\u00013\u0002\u0004\u0004\u0003\u0002\u0003\u0003\u0013B\u0016\u0016n\n\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u0014(q\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpoly_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpoly_poly.mlt",
    "content": "L\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0005\u00021\u0002\u0001\u0001\u00022\u0002\u0004\u0004\u0001\u0002\u0001\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001fn\n\u0006.\"\u000b\u0001\u0003\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003#u\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpoly_poly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_mline.mlt",
    "content": "@\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0001\u0001\u00013\u0002\u0003\u0003\u0003\u0002\u0003\u0013B\u0016\u0017n\n\u0006.\"\u000b\u0001\u0006\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_mpoly.mlt",
    "content": "K\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u001e\u001fn\n\u0006.\"\u000b\u0001\u0006\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_mpt_poly.mlt",
    "content": ";\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00021\u0002\u0001\u0001\u00032\u0002\u0002\u0002\u0001\u00013\u0002\u0002\u0002\u0003\u0003\u0013B\u0012\u0013n\n\u0006.\"\u000b\u0001\u0006\u001e \u00048@\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mpoly.mlt",
    "content": "R\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B$$n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mpoly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_mpt.mlt",
    "content": "A\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0002\u0002\u0001\u00023\u0002\u0003\u0003\u0003\u0003\u0003\u0013B\u0018\u0018n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_poly.mlt",
    "content": ">\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0002\u0002\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u0018\u0018n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b>a\u0006.\"\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_polyh_poly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_poly_pt_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_line_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_line_polyh.mlt",
    "content": "D\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0002\u0001\u00022\u0002\u0002\u0002\u0002\u00023\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001ehFK(\\\"7+\b\u0014\u0017\u000b%\u001f\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mline_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpoly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpoly_polyh.mlt",
    "content": "Y\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0005\u00021\u0002\u0001\u0001\u00022\u0002\u0004\u0004\u0002\u0002\u0001\u00023\u0002\u0007\u0007\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B**hFK(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003)9K(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpoly_polyh_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_mline.mlt",
    "content": "F\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0001\u0001\u00023\u0002\u0004\u0004\u0003\u0003\u0002\u0003\u0013B\u001c\u001chFK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_mpoly.mlt",
    "content": "Q\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B$$hFK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_mpt_polyh.mlt",
    "content": "H\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0003\u0003\u0002\u0003\u00021\u0002\u0001\u0001\u00032\u0002\u0002\u0002\u0002\u00023\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u001e\u001ehFK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048:CK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_poly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_poly_polyh.mlt",
    "content": "E\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0003\u0003\u0002\u0002\u00022\u0002\u0003\u0003\u0002\u0001\u00023\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B\u001e\u001ehFK(\\\"7+\b\u0014\u0017\u000b>a\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_poly_polyh_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_polyh_pt_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_line_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mline_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mpoly_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_mpt_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_poly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_poly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_poly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_poly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_poly_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_3_pt_polyh_pt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_mpt_mline_mpoly.mlt",
    "content": "W\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0002\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0002\u0003\u0003\u0003\u0003\u0013B((\nL\u000e\u000e\u00052\u0005Y\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mline_mpoly.mlt",
    "content": "Y\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0004\u00051\u0002\u0002\u0002\u0002\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B()\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000be\u0017$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mpt_mline.mlt",
    "content": "I\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0001\u0001\u00013\u0002\u0004\u0004\u0003\u0003\u0002\u0003\u0013B\u001c\u001e\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001\u0006\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_mpt_mpoly.mlt",
    "content": "T\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0005\u0005\u0003\u0003\u0003\u0003\u0003\u0013B$&\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001\u0006\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_polyh_mpoly.mlt",
    "content": "[\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0002\u00051\u0002\u0001\u0001\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B*+\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b!C\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_poly_polyh_mpt.mlt",
    "content": "J\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0002\u00031\u0002\u0001\u0001\u00032\u0002\u0002\u0002\u0001\u00023\u0002\u0004\u0004\u0003\u0003\u0003\u0003\u0013B\u001e\u001f\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_polyh_mpt_mline.mlt",
    "content": "N\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0001\u0001\u00023\u0002\u0005\u0005\u0003\u0003\u0003\u0002\u0003\u0013B\"\"\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_line_polyh_mpt_mpoly.mlt",
    "content": "Y\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0001\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B**\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_mpt_mline_mpoly.mlt",
    "content": "Y\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0006\u0006\u0003\u0002\u0003\u0003\u0003\u0003\u0013B()n\n\u0006.\"\u000b\u0001\u0006\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_polyh_mpt_mline.mlt",
    "content": "O\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0002\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0002\u0002\u0001\u00023\u0002\u0005\u0005\u0003\u0003\u0003\u0002\u0003\u0013B\"\"n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_poly_polyh_mpt_mpoly.mlt",
    "content": "Z\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0002\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0003\u0003\u0013B**n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_polyh_mpt_mline_mpoly.mlt",
    "content": "_\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0004\u0004\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B..hFK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_poly_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_poly_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_poly_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_poly_polyh.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_line_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_poly_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_4_pt_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_mpt_mline_mpoly.mlt",
    "content": "b\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0005\u0005\u0001\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0003\u0003\u0001\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B.0\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b\u0001\u0006\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_polyh_mpt_mline.mlt",
    "content": "X\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0005\u0005\u0001\u0002\u0002\u0003\u00041\u0002\u0002\u0002\u0003\u00022\u0002\u0002\u0002\u0001\u00023\u0002\u0006\u0006\u0003\u0003\u0003\u0003\u0002\u0003\u0013B()\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u0014"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_poly_polyh_mpt_mpoly.mlt",
    "content": "c\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0005\u0005\u0001\u0002\u0002\u0003\u00051\u0002\u0002\u0002\u0003\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\u0007\u0007\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0013B01\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u001fa\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_line_polyh_mpt_mline_mpoly.mlt",
    "content": "g\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0005\u0005\u0001\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0003\u0003\u0002\u0002\u00013\u0002\b\b\u0003\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B44\nL\u000e\u000e\u00052VEK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_5_poly_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_poly_polyh_mpt_mline_mpoly.mlt",
    "content": "h\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0005\u0005\u0002\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\b\b\u0003\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B44n\n\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_polyh_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_polyh_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_poly_polyh_mpt.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_line_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_poly_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_poly_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_poly_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_poly_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_5_pt_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_line_poly_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_line_poly_polyh_mpt_mline_mpoly.mlt",
    "content": "q\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0006\u0006\u0001\u0002\u0002\u0003\u0004\u00051\u0002\u0003\u0003\u0003\u0002\u00022\u0002\u0004\u0004\u0001\u0002\u0002\u00013\u0002\t\t\u0003\u0003\u0003\u0003\u0002\u0003\u0003\u0003\u0003\u0013B:;\nL\u000e\u000e\u00052\\\u0001\u0006.\"\u000b-\u001aK(\\\"7+\b\u0014\u0017\u000b#9\u001e \u00048\u0002u$\u0010\u0017$$ \u0019\u00147S\u001c\u0016\n+\u0015\u0016\n\t\u0003\u0014f@\u0004\u0012\u0004\u0003"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_line_poly_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_line_poly_polyh_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_line_poly_polyh_mpt_mline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_line_poly_polyh_mpt_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_line_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_6_pt_poly_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/mix_7_pt_line_poly_polyh_mpt_mline_mpoly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          38,\n          29\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"LineString\",\n        \"coordinates\": [\n          [\n            5,\n            38\n          ],\n          [\n            12,\n            45\n          ],\n          [\n            9,\n            70\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            6,\n            25\n          ],\n          [\n            21,\n            41\n          ],\n          [\n            23,\n            69\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              24,\n              10\n            ],\n            [\n              42,\n              18\n            ]\n          ],\n          [\n            [\n              30,\n              36\n            ],\n            [\n              48,\n              52\n            ],\n            [\n              35,\n              62\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                7,\n                20\n              ],\n              [\n                21,\n                31\n              ],\n              [\n                26,\n                9\n              ],\n              [\n                7,\n                20\n              ]\n            ],\n            [\n              [\n                15,\n                20\n              ],\n              [\n                20,\n                15\n              ],\n              [\n                18,\n                25\n              ],\n              [\n                15,\n                20\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                69,\n                57\n              ],\n              [\n                71,\n                66\n              ],\n              [\n                73,\n                64\n              ],\n              [\n                69,\n                57\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/multiline.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ]\n          ],\n          [\n            [\n              23,\n              34\n            ],\n            [\n              73,\n              4\n            ],\n            [\n              13,\n              24\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/multiline.mlt",
    "content": ".\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0001\u0001\u00041\u0002\u0001\u0001\u00022b\u0002\u0002\u0001\u0002\u0002\u0003\u0013B\f\f\u0016hx(\u0013cK\u0018d;w("
  },
  {
    "path": "test/synthetic/0x01/multiline_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiLineString\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              8,\n              0\n            ],\n            [\n              0,\n              8\n            ],\n            [\n              8,\n              8\n            ],\n            [\n              16,\n              0\n            ],\n            [\n              24,\n              0\n            ],\n            [\n              16,\n              8\n            ],\n            [\n              24,\n              8\n            ]\n          ],\n          [\n            [\n              0,\n              16\n            ],\n            [\n              8,\n              16\n            ],\n            [\n              0,\n              24\n            ],\n            [\n              8,\n              24\n            ],\n            [\n              16,\n              16\n            ],\n            [\n              24,\n              16\n            ],\n            [\n              16,\n              24\n            ],\n            [\n              24,\n              24\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/multipoint.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            11,\n            52\n          ],\n          [\n            71,\n            72\n          ],\n          [\n            61,\n            22\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/multipoint.mlt",
    "content": " \u0001\u0006layer1P\u0001\u0004\u00030\u0002\u0001\u0001\u00031\u0002\u0001\u0001\u0003\u0013B\u0006\u0006\u0016hx(\u0013c"
  },
  {
    "path": "test/synthetic/0x01/multipoint_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPoint\",\n        \"coordinates\": [\n          [\n            0,\n            0\n          ],\n          [\n            8,\n            0\n          ],\n          [\n            0,\n            8\n          ],\n          [\n            8,\n            8\n          ],\n          [\n            16,\n            0\n          ],\n          [\n            24,\n            0\n          ],\n          [\n            16,\n            8\n          ],\n          [\n            24,\n            8\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/point.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly.mlt",
    "content": "%\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0001\u0001\u00022\u0002\u0001\u0001\u00013\u0002\u0001\u0001\u0003\u0013B\u0006\u0006\u0016hx(\u0013c"
  },
  {
    "path": "test/synthetic/0x01/poly_collinear.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_collinear_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_collinear_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_collinear_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole.mlt",
    "content": ".\u0001\u0006layer1P\u0001\u0004\u00040\u0002\u0001\u0001\u00022\u0002\u0001\u0001\u00023b\u0002\u0002\u0001\u0002\u0002\u0003\u0013B\f\f\u0016hx(\u0013c\bX;\u0013('"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_touching.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_touching_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_touching_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_hole_touching_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_morton_hole_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              8,\n              0\n            ],\n            [\n              0,\n              8\n            ],\n            [\n              8,\n              8\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              16,\n              0\n            ],\n            [\n              24,\n              0\n            ],\n            [\n              16,\n              8\n            ],\n            [\n              24,\n              8\n            ],\n            [\n              0,\n              16\n            ],\n            [\n              8,\n              16\n            ],\n            [\n              0,\n              24\n            ],\n            [\n              8,\n              24\n            ],\n            [\n              16,\n              16\n            ],\n            [\n              24,\n              16\n            ],\n            [\n              16,\n              24\n            ],\n            [\n              24,\n              24\n            ],\n            [\n              16,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_morton_ring_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              8,\n              0\n            ],\n            [\n              0,\n              8\n            ],\n            [\n              8,\n              8\n            ],\n            [\n              16,\n              0\n            ],\n            [\n              24,\n              0\n            ],\n            [\n              16,\n              8\n            ],\n            [\n              24,\n              8\n            ],\n            [\n              0,\n              16\n            ],\n            [\n              8,\n              16\n            ],\n            [\n              0,\n              24\n            ],\n            [\n              8,\n              24\n            ],\n            [\n              16,\n              16\n            ],\n            [\n              24,\n              16\n            ],\n            [\n              16,\n              24\n            ],\n            [\n              24,\n              24\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_morton_ring_no_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              8,\n              0\n            ],\n            [\n              0,\n              8\n            ],\n            [\n              8,\n              8\n            ],\n            [\n              16,\n              0\n            ],\n            [\n              24,\n              0\n            ],\n            [\n              16,\n              8\n            ],\n            [\n              24,\n              8\n            ],\n            [\n              0,\n              16\n            ],\n            [\n              8,\n              16\n            ],\n            [\n              0,\n              24\n            ],\n            [\n              8,\n              24\n            ],\n            [\n              16,\n              16\n            ],\n            [\n              24,\n              16\n            ],\n            [\n              16,\n              24\n            ],\n            [\n              24,\n              24\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                11,\n                52\n              ],\n              [\n                71,\n                72\n              ],\n              [\n                61,\n                22\n              ],\n              [\n                11,\n                52\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                23,\n                34\n              ],\n              [\n                73,\n                4\n              ],\n              [\n                13,\n                24\n              ],\n              [\n                23,\n                34\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi.mlt",
    "content": "6\u0001\u0006layer1P\u0001\u0004\u00050\u0002\u0001\u0001\u00051\u0002\u0001\u0001\u00022b\u0002\u0002\u0001\u0002\u0002\u00013b\u0002\u0002\u0001\u0002\u0002\u0003\u0013B\f\f\u0016hx(\u0013cK\u0018d;w("
  },
  {
    "path": "test/synthetic/0x01/poly_multi_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                11,\n                52\n              ],\n              [\n                71,\n                72\n              ],\n              [\n                61,\n                22\n              ],\n              [\n                11,\n                52\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                23,\n                34\n              ],\n              [\n                73,\n                4\n              ],\n              [\n                13,\n                24\n              ],\n              [\n                23,\n                34\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                11,\n                52\n              ],\n              [\n                71,\n                72\n              ],\n              [\n                61,\n                22\n              ],\n              [\n                11,\n                52\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                23,\n                34\n              ],\n              [\n                73,\n                4\n              ],\n              [\n                13,\n                24\n              ],\n              [\n                23,\n                34\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi_morton_hole_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                0,\n                0\n              ],\n              [\n                8,\n                0\n              ],\n              [\n                0,\n                8\n              ],\n              [\n                8,\n                8\n              ],\n              [\n                0,\n                0\n              ]\n            ],\n            [\n              [\n                16,\n                0\n              ],\n              [\n                24,\n                0\n              ],\n              [\n                16,\n                8\n              ],\n              [\n                24,\n                8\n              ],\n              [\n                0,\n                16\n              ],\n              [\n                8,\n                16\n              ],\n              [\n                0,\n                24\n              ],\n              [\n                8,\n                24\n              ],\n              [\n                16,\n                16\n              ],\n              [\n                24,\n                16\n              ],\n              [\n                16,\n                24\n              ],\n              [\n                24,\n                24\n              ],\n              [\n                16,\n                0\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi_morton_ring_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                0,\n                0\n              ],\n              [\n                8,\n                0\n              ],\n              [\n                0,\n                8\n              ],\n              [\n                8,\n                8\n              ],\n              [\n                16,\n                0\n              ],\n              [\n                24,\n                0\n              ],\n              [\n                16,\n                8\n              ],\n              [\n                24,\n                8\n              ],\n              [\n                0,\n                0\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                0,\n                16\n              ],\n              [\n                8,\n                16\n              ],\n              [\n                0,\n                24\n              ],\n              [\n                8,\n                24\n              ],\n              [\n                16,\n                16\n              ],\n              [\n                24,\n                16\n              ],\n              [\n                16,\n                24\n              ],\n              [\n                24,\n                24\n              ],\n              [\n                0,\n                16\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi_morton_ring_no_morton.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                0,\n                0\n              ],\n              [\n                8,\n                0\n              ],\n              [\n                0,\n                8\n              ],\n              [\n                8,\n                8\n              ],\n              [\n                16,\n                0\n              ],\n              [\n                24,\n                0\n              ],\n              [\n                16,\n                8\n              ],\n              [\n                24,\n                8\n              ],\n              [\n                0,\n                0\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                0,\n                16\n              ],\n              [\n                8,\n                16\n              ],\n              [\n                0,\n                24\n              ],\n              [\n                8,\n                24\n              ],\n              [\n                16,\n                16\n              ],\n              [\n                24,\n                16\n              ],\n              [\n                16,\n                24\n              ],\n              [\n                24,\n                24\n              ],\n              [\n                0,\n                16\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_multi_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"MultiPolygon\",\n        \"coordinates\": [\n          [\n            [\n              [\n                11,\n                52\n              ],\n              [\n                71,\n                72\n              ],\n              [\n                61,\n                22\n              ],\n              [\n                11,\n                52\n              ]\n            ]\n          ],\n          [\n            [\n              [\n                23,\n                34\n              ],\n              [\n                73,\n                4\n              ],\n              [\n                13,\n                24\n              ],\n              [\n                23,\n                34\n              ]\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_self_intersect.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_self_intersect_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_self_intersect_fpf_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_self_intersect_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/poly_tes.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool_false.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": false\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool_false_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": false\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool_null_false.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": false\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool_null_true.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_bool_true_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_empty_name.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"\": true,\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.14\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.4028235E38\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_min_norm.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.1754944E-38\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_min_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.4E-45\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_nan.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::NAN\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_neg_inf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::NEG_INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_neg_zero.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.14\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_pos_inf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.14\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f32_zero.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.141592653589793\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.7976931348623157E308\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_min_norm.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2.2250738585072014E-308\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_min_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 4.9E-324\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_nan.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::NAN\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_neg_inf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::NEG_INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_neg_zero.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.141592653589793\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_pos_inf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.141592653589793\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_f64_zero.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2147483647\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32_min.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -2147483648\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32_neg.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i32_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9223372036854775807\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64_min.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -9223372036854775808\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64_neg.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_i64_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_special_name.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"hello\\u0000 world\\n\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_ascii.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"42\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_empty.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_empty_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_escape.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"Line1\\n\\t\\\"quoted\\\"\\\\path\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"42\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_special.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"hello\\u0000 world\\n\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_unicode.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"München 📍 café\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_val_empty.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_str_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"42\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u32.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u32_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 4294967295\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u32_min.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u32_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u32_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 1234567890123456789\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u64_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 18446744073709551615\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u64_min.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u64_null_val.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1234567890123456789\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/prop_u64_val_null.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1234567890123456789\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i32.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i32_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i32_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i32_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i64_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i64_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_i64_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_mixed.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"active\": true,\n        \"biggest\": 0,\n        \"bignum\": 42,\n        \"count\": 42,\n        \"medium\": 100,\n        \"name\": \"Test Point\",\n        \"precision\": 0.123456789,\n        \"temp\": 25.5\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_no_shared_dict.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_offset_str.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_offset_str_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_2_same_prefix.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:he\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name_en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name_fr\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_no_child_name.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_no_child_name_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_no_struct_name.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_no_struct_name_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_one_child.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"place\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_shared_dict_one_child_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"place\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_str.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_north_sector_1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"commercial_zone_south_sector_2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"industrial_zone_east_sector_3\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"park_zone_west_sector_4\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          65,\n          66\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"water_zone_north_sector_5\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          35,\n          56\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_south_sector_6\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          55,\n          36\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_str_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_north_sector_1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"commercial_zone_south_sector_2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"industrial_zone_east_sector_3\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"park_zone_west_sector_4\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          65,\n          66\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"water_zone_north_sector_5\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          35,\n          56\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_south_sector_6\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          55,\n          36\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_127.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_128.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_129.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_255.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_256.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_257.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_383.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_384.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_385.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_511.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_512.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_fpf_513.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u32_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u64.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u64_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u64_delta_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01/props_u64_rle.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/id64_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 18446744073709551615,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/id_max.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 4294967295,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/ids64_minmax.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 0,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 18446744073709551615,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 0,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 18446744073709551615,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/ids64_minmax_delta.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 0,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 18446744073709551615,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 0,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 18446744073709551615,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/ids_delta_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/ids_fpf.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"id\": 103,\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/mix_2_poly_poly_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/mix_2_poly_polyh_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/mix_2_polyh_polyh_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/mix_3_poly_polyh_poly_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/mix_3_polyh_poly_polyh_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              55,\n              5\n            ],\n            [\n              58,\n              28\n            ],\n            [\n              75,\n              22\n            ],\n            [\n              55,\n              5\n            ]\n          ]\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              52,\n              35\n            ],\n            [\n              14,\n              55\n            ],\n            [\n              60,\n              72\n            ],\n            [\n              52,\n              35\n            ]\n          ],\n          [\n            [\n              32,\n              50\n            ],\n            [\n              36,\n              60\n            ],\n            [\n              24,\n              54\n            ],\n            [\n              32,\n              50\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_collinear_fpf_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_collinear_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              20,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_fpf_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_hole_fpf_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_hole_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ],\n          [\n            [\n              65,\n              66\n            ],\n            [\n              35,\n              56\n            ],\n            [\n              55,\n              36\n            ],\n            [\n              65,\n              66\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_hole_touching_fpf_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_hole_touching_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              0,\n              0\n            ]\n          ],\n          [\n            [\n              0,\n              0\n            ],\n            [\n              2,\n              2\n            ],\n            [\n              5,\n              2\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_self_intersect_fpf_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_self_intersect_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              0,\n              0\n            ],\n            [\n              10,\n              10\n            ],\n            [\n              0,\n              10\n            ],\n            [\n              10,\n              0\n            ],\n            [\n              0,\n              0\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/poly_tes_ns.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Polygon\",\n        \"coordinates\": [\n          [\n            [\n              11,\n              52\n            ],\n            [\n              71,\n              72\n            ],\n            [\n              61,\n              22\n            ],\n            [\n              11,\n              52\n            ]\n          ]\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_bool_false_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": false\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_bool_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_empty_name_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"\": true,\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.4028234663852886e+38\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_min_norm_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.1754943508222875e-38\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_min_val_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.401298464324817e-45\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_nan_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::NAN\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_neg_inf_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::NEG_INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_neg_zero_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.140000104904175\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_pos_inf_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f32::INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f32_zero_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1.7976931348623157e+308\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_min_norm_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2.2250738585072014e-308\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_min_val_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 5e-324\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_nan_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::NAN\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_neg_inf_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::NEG_INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_neg_zero_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 3.141592653589793\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_pos_inf_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"f64::INFINITY\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_f64_zero_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0.0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i32_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2147483647\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i32_min_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -2147483648\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i32_neg_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i32_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i64_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9223372036854775807\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i64_min_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -9223372036854775808\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i64_neg_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": -9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_i64_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9876543210\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_special_name_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"hello\\u0000 world\\n\": true\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_str_ascii_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"42\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_str_empty_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_str_escape_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"Line1\\n\\t\\\"quoted\\\"\\\\path\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_str_special_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"hello\\u0000 world\\n\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_str_unicode_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"München 📍 café\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u32_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 4294967295\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u32_min_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u32_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u64_max_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 18446744073709551615\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u64_min_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/prop_u64_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"bignum\": 1234567890123456789\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_i32_delta_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_i32_delta_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_i32_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_i32_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 42\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_mixed_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"active\": true,\n        \"biggest\": 0,\n        \"bignum\": 42,\n        \"count\": 42,\n        \"medium\": 100,\n        \"name\": \"Test Point\",\n        \"precision\": 0.123456789,\n        \"temp\": 25.5\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_no_shared_dict_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_offset_str_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_offset_str_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_offset_str_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_2_same_prefix_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:he\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name_en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name_fr\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_child_name_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_child_name_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_child_name_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_struct_name_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_struct_name_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_no_struct_name_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:de\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_one_child_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"place\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_one_child_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"place\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_one_child_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"name:en\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"place\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_presence_variants.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"6-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"8-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"6-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"8-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_shared_dict_presence_variants_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"6-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"8-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"1-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"1-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"2-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"3-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"4-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"5-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"6-a\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"7-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"8-b\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\",\n        \"_extent\": 80,\n        \"_layer\": \"layer1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_str_fsst.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_north_sector_1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"commercial_zone_south_sector_2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"industrial_zone_east_sector_3\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"park_zone_west_sector_4\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          65,\n          66\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"water_zone_north_sector_5\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          35,\n          56\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_south_sector_6\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          55,\n          36\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_str_fsst_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_north_sector_1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"commercial_zone_south_sector_2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"industrial_zone_east_sector_3\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"park_zone_west_sector_4\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          65,\n          66\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"water_zone_north_sector_5\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          35,\n          56\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_south_sector_6\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          55,\n          36\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_str_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_north_sector_1\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"commercial_zone_south_sector_2\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"industrial_zone_east_sector_3\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"park_zone_west_sector_4\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          65,\n          66\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"water_zone_north_sector_5\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          35,\n          56\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": \"residential_zone_south_sector_6\"\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          55,\n          36\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_delta_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_delta_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_127_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_128_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_129_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_255_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_256_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_257_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_383_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_384_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_385_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_511_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_512_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_fpf_513_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 0\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 1\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 2\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u32_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u64_delta_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u64_delta_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u64_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/0x01-rust/props_u64_rle_np.json",
    "content": "{\n  \"type\": \"FeatureCollection\",\n  \"features\": [\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          13,\n          42\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          11,\n          52\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          71,\n          72\n        ]\n      }\n    },\n    {\n      \"type\": \"Feature\",\n      \"properties\": {\n        \"_extent\": 80,\n        \"_layer\": \"layer1\",\n        \"val\": 9000\n      },\n      \"geometry\": {\n        \"type\": \"Point\",\n        \"coordinates\": [\n          61,\n          22\n        ]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "test/synthetic/java.txt",
    "content": "mix_2_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mline--Length(Geometries)\tNone\nmix_2_line_mline--Length(Parts)\tNone\nmix_2_line_mline--Length(VarBinary)\tNone\nmix_2_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mpoly--Length(Geometries)\tNone\nmix_2_line_mpoly--Length(Parts)\tNone\nmix_2_line_mpoly--Length(Rings)\tRle\nmix_2_line_mpoly--Length(VarBinary)\tNone\nmix_2_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mpt--Length(Geometries)\tNone\nmix_2_line_mpt--Length(Parts)\tNone\nmix_2_line_mpt--Length(VarBinary)\tNone\nmix_2_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_2_line_poly--Length(Parts)\tNone\nmix_2_line_poly--Length(Rings)\tRle\nmix_2_line_poly--Length(VarBinary)\tNone\nmix_2_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_line_polyh--Length(Parts)\tNone\nmix_2_line_polyh--Length(Rings)\tRle\nmix_2_line_polyh--Length(VarBinary)\tNone\nmix_2_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_mline_mpoly--Length(Geometries)\tRle\nmix_2_mline_mpoly--Length(Parts)\tNone\nmix_2_mline_mpoly--Length(Rings)\tRle\nmix_2_mline_mpoly--Length(VarBinary)\tNone\nmix_2_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_mpt_mline--Length(Geometries)\tNone\nmix_2_mpt_mline--Length(Parts)\tNone\nmix_2_mpt_mline--Length(VarBinary)\tNone\nmix_2_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_mpt_mpoly--Length(Geometries)\tNone\nmix_2_mpt_mpoly--Length(Parts)\tNone\nmix_2_mpt_mpoly--Length(Rings)\tRle\nmix_2_mpt_mpoly--Length(VarBinary)\tNone\nmix_2_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mline--Length(Geometries)\tNone\nmix_2_poly_mline--Length(Parts)\tNone\nmix_2_poly_mline--Length(Rings)\tNone\nmix_2_poly_mline--Length(VarBinary)\tNone\nmix_2_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mpoly--Length(Geometries)\tNone\nmix_2_poly_mpoly--Length(Parts)\tNone\nmix_2_poly_mpoly--Length(Rings)\tRle\nmix_2_poly_mpoly--Length(VarBinary)\tNone\nmix_2_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mpt--Length(Geometries)\tNone\nmix_2_poly_mpt--Length(Parts)\tNone\nmix_2_poly_mpt--Length(Rings)\tNone\nmix_2_poly_mpt--Length(VarBinary)\tNone\nmix_2_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_polyh--Length(Parts)\tNone\nmix_2_poly_polyh--Length(Rings)\tRle\nmix_2_poly_polyh--Length(VarBinary)\tRle\nmix_2_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mline--Length(Geometries)\tNone\nmix_2_polyh_mline--Length(Parts)\tNone\nmix_2_polyh_mline--Length(Rings)\tNone\nmix_2_polyh_mline--Length(VarBinary)\tNone\nmix_2_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mpoly--Length(Geometries)\tNone\nmix_2_polyh_mpoly--Length(Parts)\tNone\nmix_2_polyh_mpoly--Length(Rings)\tRle\nmix_2_polyh_mpoly--Length(VarBinary)\tNone\nmix_2_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mpt--Length(Geometries)\tNone\nmix_2_polyh_mpt--Length(Parts)\tNone\nmix_2_polyh_mpt--Length(Rings)\tRle\nmix_2_polyh_mpt--Length(VarBinary)\tNone\nmix_2_pt_line--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_line--Length(Parts)\tNone\nmix_2_pt_line--Length(VarBinary)\tNone\nmix_2_pt_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mline--Length(Geometries)\tNone\nmix_2_pt_mline--Length(Parts)\tNone\nmix_2_pt_mline--Length(VarBinary)\tNone\nmix_2_pt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mpoly--Length(Geometries)\tNone\nmix_2_pt_mpoly--Length(Parts)\tNone\nmix_2_pt_mpoly--Length(Rings)\tRle\nmix_2_pt_mpoly--Length(VarBinary)\tNone\nmix_2_pt_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mpt--Length(Geometries)\tNone\nmix_2_pt_mpt--Length(VarBinary)\tNone\nmix_2_pt_poly--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_poly--Length(Parts)\tNone\nmix_2_pt_poly--Length(Rings)\tNone\nmix_2_pt_poly--Length(VarBinary)\tNone\nmix_2_pt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_polyh--Length(Parts)\tNone\nmix_2_pt_polyh--Length(Rings)\tRle\nmix_2_pt_polyh--Length(VarBinary)\tNone\nmix_3_line_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mline_mpoly--Length(Geometries)\tRle\nmix_3_line_mline_mpoly--Length(Parts)\tNone\nmix_3_line_mline_mpoly--Length(Rings)\tNone\nmix_3_line_mline_mpoly--Length(VarBinary)\tNone\nmix_3_line_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mpt_mline--Length(Geometries)\tNone\nmix_3_line_mpt_mline--Length(Parts)\tNone\nmix_3_line_mpt_mline--Length(VarBinary)\tNone\nmix_3_line_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mpt_mpoly--Length(Geometries)\tNone\nmix_3_line_mpt_mpoly--Length(Parts)\tNone\nmix_3_line_mpt_mpoly--Length(Rings)\tRle\nmix_3_line_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_line_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mline--Length(Geometries)\tNone\nmix_3_line_poly_mline--Length(Parts)\tNone\nmix_3_line_poly_mline--Length(Rings)\tNone\nmix_3_line_poly_mline--Length(VarBinary)\tNone\nmix_3_line_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mpoly--Length(Geometries)\tNone\nmix_3_line_poly_mpoly--Length(Parts)\tNone\nmix_3_line_poly_mpoly--Length(Rings)\tRle\nmix_3_line_poly_mpoly--Length(VarBinary)\tNone\nmix_3_line_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mpt--Length(Geometries)\tNone\nmix_3_line_poly_mpt--Length(Parts)\tNone\nmix_3_line_poly_mpt--Length(Rings)\tRle\nmix_3_line_poly_mpt--Length(VarBinary)\tDeltaRle\nmix_3_line_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_polyh--Length(Parts)\tNone\nmix_3_line_poly_polyh--Length(Rings)\tRle\nmix_3_line_poly_polyh--Length(VarBinary)\tNone\nmix_3_line_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mline--Length(Geometries)\tNone\nmix_3_line_polyh_mline--Length(Parts)\tNone\nmix_3_line_polyh_mline--Length(Rings)\tNone\nmix_3_line_polyh_mline--Length(VarBinary)\tNone\nmix_3_line_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mpoly--Length(Geometries)\tNone\nmix_3_line_polyh_mpoly--Length(Parts)\tNone\nmix_3_line_polyh_mpoly--Length(Rings)\tRle\nmix_3_line_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_line_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mpt--Length(Geometries)\tNone\nmix_3_line_polyh_mpt--Length(Parts)\tNone\nmix_3_line_polyh_mpt--Length(Rings)\tRle\nmix_3_line_polyh_mpt--Length(VarBinary)\tDeltaRle\nmix_3_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_3_mpt_mline_mpoly--Length(Parts)\tNone\nmix_3_mpt_mline_mpoly--Length(Rings)\tRle\nmix_3_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_3_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mline_mpoly--Length(Geometries)\tRle\nmix_3_poly_mline_mpoly--Length(Parts)\tNone\nmix_3_poly_mline_mpoly--Length(Rings)\tNone\nmix_3_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_3_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mpt_mline--Length(Geometries)\tNone\nmix_3_poly_mpt_mline--Length(Parts)\tNone\nmix_3_poly_mpt_mline--Length(Rings)\tNone\nmix_3_poly_mpt_mline--Length(VarBinary)\tNone\nmix_3_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_3_poly_mpt_mpoly--Length(Parts)\tNone\nmix_3_poly_mpt_mpoly--Length(Rings)\tRle\nmix_3_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mline--Length(Geometries)\tNone\nmix_3_poly_polyh_mline--Length(Parts)\tNone\nmix_3_poly_polyh_mline--Length(Rings)\tNone\nmix_3_poly_polyh_mline--Length(VarBinary)\tNone\nmix_3_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_3_poly_polyh_mpoly--Length(Parts)\tNone\nmix_3_poly_polyh_mpoly--Length(Rings)\tRle\nmix_3_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mpt--Length(Geometries)\tNone\nmix_3_poly_polyh_mpt--Length(Parts)\tNone\nmix_3_poly_polyh_mpt--Length(Rings)\tRle\nmix_3_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_3_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_3_polyh_mline_mpoly--Length(Parts)\tNone\nmix_3_polyh_mline_mpoly--Length(Rings)\tRle\nmix_3_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_3_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mpt_mline--Length(Geometries)\tNone\nmix_3_polyh_mpt_mline--Length(Parts)\tNone\nmix_3_polyh_mpt_mline--Length(Rings)\tNone\nmix_3_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_3_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_3_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_3_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_3_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_pt_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mline--Length(Geometries)\tNone\nmix_3_pt_line_mline--Length(Parts)\tNone\nmix_3_pt_line_mline--Length(VarBinary)\tNone\nmix_3_pt_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mpoly--Length(Geometries)\tNone\nmix_3_pt_line_mpoly--Length(Parts)\tNone\nmix_3_pt_line_mpoly--Length(Rings)\tRle\nmix_3_pt_line_mpoly--Length(VarBinary)\tNone\nmix_3_pt_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mpt--Length(Geometries)\tNone\nmix_3_pt_line_mpt--Length(Parts)\tNone\nmix_3_pt_line_mpt--Length(VarBinary)\tNone\nmix_3_pt_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_poly--Length(Parts)\tNone\nmix_3_pt_line_poly--Length(Rings)\tRle\nmix_3_pt_line_poly--Length(VarBinary)\tNone\nmix_3_pt_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_polyh--Length(Parts)\tNone\nmix_3_pt_line_polyh--Length(Rings)\tRle\nmix_3_pt_line_polyh--Length(VarBinary)\tNone\nmix_3_pt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mline_mpoly--Length(Geometries)\tRle\nmix_3_pt_mline_mpoly--Length(Parts)\tNone\nmix_3_pt_mline_mpoly--Length(Rings)\tRle\nmix_3_pt_mline_mpoly--Length(VarBinary)\tNone\nmix_3_pt_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mpt_mline--Length(Geometries)\tNone\nmix_3_pt_mpt_mline--Length(Parts)\tNone\nmix_3_pt_mpt_mline--Length(VarBinary)\tNone\nmix_3_pt_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mpt_mpoly--Length(Geometries)\tNone\nmix_3_pt_mpt_mpoly--Length(Parts)\tNone\nmix_3_pt_mpt_mpoly--Length(Rings)\tRle\nmix_3_pt_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_pt_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mline--Length(Geometries)\tNone\nmix_3_pt_poly_mline--Length(Parts)\tNone\nmix_3_pt_poly_mline--Length(Rings)\tNone\nmix_3_pt_poly_mline--Length(VarBinary)\tNone\nmix_3_pt_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mpoly--Length(Geometries)\tNone\nmix_3_pt_poly_mpoly--Length(Parts)\tNone\nmix_3_pt_poly_mpoly--Length(Rings)\tRle\nmix_3_pt_poly_mpoly--Length(VarBinary)\tNone\nmix_3_pt_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mpt--Length(Geometries)\tNone\nmix_3_pt_poly_mpt--Length(Parts)\tNone\nmix_3_pt_poly_mpt--Length(Rings)\tNone\nmix_3_pt_poly_mpt--Length(VarBinary)\tNone\nmix_3_pt_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_polyh--Length(Parts)\tNone\nmix_3_pt_poly_polyh--Length(Rings)\tRle\nmix_3_pt_poly_polyh--Length(VarBinary)\tNone\nmix_3_pt_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mline--Length(Geometries)\tNone\nmix_3_pt_polyh_mline--Length(Parts)\tNone\nmix_3_pt_polyh_mline--Length(Rings)\tNone\nmix_3_pt_polyh_mline--Length(VarBinary)\tNone\nmix_3_pt_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mpoly--Length(Geometries)\tNone\nmix_3_pt_polyh_mpoly--Length(Parts)\tNone\nmix_3_pt_polyh_mpoly--Length(Rings)\tRle\nmix_3_pt_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_pt_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mpt--Length(Geometries)\tNone\nmix_3_pt_polyh_mpt--Length(Parts)\tNone\nmix_3_pt_polyh_mpt--Length(Rings)\tRle\nmix_3_pt_polyh_mpt--Length(VarBinary)\tNone\nmix_4_line_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_line_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_line_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_line_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mline_mpoly--Length(Geometries)\tRle\nmix_4_line_poly_mline_mpoly--Length(Parts)\tNone\nmix_4_line_poly_mline_mpoly--Length(Rings)\tRle\nmix_4_line_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mpt_mline--Length(Geometries)\tNone\nmix_4_line_poly_mpt_mline--Length(Parts)\tNone\nmix_4_line_poly_mpt_mline--Length(Rings)\tNone\nmix_4_line_poly_mpt_mline--Length(VarBinary)\tDeltaRle\nmix_4_line_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_4_line_poly_mpt_mpoly--Length(Parts)\tNone\nmix_4_line_poly_mpt_mpoly--Length(Rings)\tRle\nmix_4_line_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mline--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mline--Length(Parts)\tNone\nmix_4_line_poly_polyh_mline--Length(Rings)\tNone\nmix_4_line_poly_polyh_mline--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mpoly--Length(Parts)\tNone\nmix_4_line_poly_polyh_mpoly--Length(Rings)\tRle\nmix_4_line_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mpt--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mpt--Length(Parts)\tNone\nmix_4_line_poly_polyh_mpt--Length(Rings)\tRle\nmix_4_line_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_4_line_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_4_line_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_line_polyh_mline_mpoly--Length(Rings)\tRle\nmix_4_line_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_line_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_line_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_line_polyh_mpt_mline--Length(VarBinary)\tDeltaRle\nmix_4_line_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_line_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_line_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_4_line_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_4_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_poly_polyh_mline_mpoly--Length(Rings)\tRle\nmix_4_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_4_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_poly_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_4_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_4_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mline_mpoly--Length(Geometries)\tRle\nmix_4_pt_line_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_line_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_line_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_line_mpt_mline--Length(Parts)\tNone\nmix_4_pt_line_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_line_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_line_mpt_mpoly--Length(Rings)\tRle\nmix_4_pt_line_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mline--Length(Geometries)\tNone\nmix_4_pt_line_poly_mline--Length(Parts)\tNone\nmix_4_pt_line_poly_mline--Length(Rings)\tNone\nmix_4_pt_line_poly_mline--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_poly_mpoly--Length(Parts)\tNone\nmix_4_pt_line_poly_mpoly--Length(Rings)\tRle\nmix_4_pt_line_poly_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mpt--Length(Geometries)\tNone\nmix_4_pt_line_poly_mpt--Length(Parts)\tNone\nmix_4_pt_line_poly_mpt--Length(Rings)\tRle\nmix_4_pt_line_poly_mpt--Length(VarBinary)\tNone\nmix_4_pt_line_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_polyh--Length(Parts)\tNone\nmix_4_pt_line_poly_polyh--Length(Rings)\tRle\nmix_4_pt_line_poly_polyh--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mline--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mline--Length(Parts)\tNone\nmix_4_pt_line_polyh_mline--Length(Rings)\tNone\nmix_4_pt_line_polyh_mline--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mpoly--Length(Parts)\tNone\nmix_4_pt_line_polyh_mpoly--Length(Rings)\tRle\nmix_4_pt_line_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mpt--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mpt--Length(Parts)\tNone\nmix_4_pt_line_polyh_mpt--Length(Rings)\tRle\nmix_4_pt_line_polyh_mpt--Length(VarBinary)\tNone\nmix_4_pt_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_pt_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_mpt_mline_mpoly--Length(Rings)\tRle\nmix_4_pt_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mline_mpoly--Length(Geometries)\tRle\nmix_4_pt_poly_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_poly_mpt_mline--Length(Parts)\tNone\nmix_4_pt_poly_mpt_mline--Length(Rings)\tNone\nmix_4_pt_poly_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_poly_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_mpt_mpoly--Length(Rings)\tRle\nmix_4_pt_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mline--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mline--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mline--Length(Rings)\tNone\nmix_4_pt_poly_polyh_mline--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mpoly--Length(Rings)\tRle\nmix_4_pt_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mpt--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mpt--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mpt--Length(Rings)\tRle\nmix_4_pt_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_4_pt_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_4_pt_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_polyh_mline_mpoly--Length(Rings)\tRle\nmix_4_pt_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_pt_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_pt_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_4_pt_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Length(Rings)\tRle\nmix_5_line_poly_mpt_mline_mpoly--Length(VarBinary)\tDeltaRle\nmix_5_line_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_5_line_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_line_poly_polyh_mline_mpoly--Length(Rings)\tRle\nmix_5_line_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_line_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_5_line_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_5_line_polyh_mpt_mline_mpoly--Length(VarBinary)\tDeltaRle\nmix_5_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_5_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mline_mpoly--Length(Geometries)\tRle\nmix_5_pt_line_poly_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_mline_mpoly--Length(Rings)\tRle\nmix_5_pt_line_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(Parts)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(Rings)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(VarBinary)\tDeltaRle\nmix_5_pt_line_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Length(Rings)\tRle\nmix_5_pt_line_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mline--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(Rings)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Length(Rings)\tRle\nmix_5_pt_line_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mpt--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mpt--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mpt--Length(Rings)\tRle\nmix_5_pt_line_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_5_pt_line_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Length(Rings)\tRle\nmix_5_pt_line_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(VarBinary)\tDeltaRle\nmix_5_pt_line_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_5_pt_line_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_5_pt_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Length(Rings)\tRle\nmix_5_pt_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_5_pt_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_5_pt_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Rings)\tRle\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(VarBinary)\tDeltaRle\nmix_6_pt_line_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Geometries)\tRle\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Rings)\tRle\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Rings)\tRle\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(VarBinary)\tDeltaRle\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Rings)\tRle\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_dup_line--Data(Vertex)\tComponentwiseDelta\nmix_dup_line--Length(Parts)\tRle\nmix_dup_line--Length(VarBinary)\tRle\nmix_dup_mline--Data(Vertex)\tComponentwiseDelta\nmix_dup_mline--Length(Geometries)\tRle\nmix_dup_mline--Length(Parts)\tNone\nmix_dup_mline--Length(VarBinary)\tRle\nmix_dup_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_dup_mpoly--Length(Geometries)\tRle\nmix_dup_mpoly--Length(Parts)\tNone\nmix_dup_mpoly--Length(Rings)\tRle\nmix_dup_mpoly--Length(VarBinary)\tRle\nmix_dup_mpt--Data(Vertex)\tComponentwiseDelta\nmix_dup_mpt--Length(Geometries)\tRle\nmix_dup_mpt--Length(VarBinary)\tRle\nmix_dup_poly--Data(Vertex)\tComponentwiseDelta\nmix_dup_poly--Length(Parts)\tRle\nmix_dup_poly--Length(Rings)\tRle\nmix_dup_poly--Length(VarBinary)\tRle\nmix_dup_polyh--Data(Vertex)\tComponentwiseDelta\nmix_dup_polyh--Length(Parts)\tRle\nmix_dup_polyh--Length(Rings)\tRle\nmix_dup_polyh--Length(VarBinary)\tRle\nmix_dup_pt--Data(Vertex)\tComponentwiseDelta\nmix_dup_pt--Length(VarBinary)\tRle\nmix_line_mline_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mline_line--Length(Geometries)\tNone\nmix_line_mline_line--Length(Parts)\tNone\nmix_line_mline_line--Length(VarBinary)\tNone\nmix_line_mpoly_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mpoly_line--Length(Geometries)\tNone\nmix_line_mpoly_line--Length(Parts)\tNone\nmix_line_mpoly_line--Length(Rings)\tRle\nmix_line_mpoly_line--Length(VarBinary)\tNone\nmix_line_mpt_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mpt_line--Length(Geometries)\tNone\nmix_line_mpt_line--Length(Parts)\tRle\nmix_line_mpt_line--Length(VarBinary)\tNone\nmix_line_poly_line--Data(Vertex)\tComponentwiseDelta\nmix_line_poly_line--Length(Parts)\tNone\nmix_line_poly_line--Length(Rings)\tRle\nmix_line_poly_line--Length(VarBinary)\tNone\nmix_line_polyh_line--Data(Vertex)\tComponentwiseDelta\nmix_line_polyh_line--Length(Parts)\tNone\nmix_line_polyh_line--Length(Rings)\tRle\nmix_line_polyh_line--Length(VarBinary)\tNone\nmix_line_pt_line--Data(Vertex)\tComponentwiseDelta\nmix_line_pt_line--Length(Parts)\tRle\nmix_line_pt_line--Length(VarBinary)\tNone\nmix_mline_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_line_mline--Length(Geometries)\tRle\nmix_mline_line_mline--Length(Parts)\tNone\nmix_mline_line_mline--Length(VarBinary)\tNone\nmix_mline_mpoly_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_mpoly_mline--Length(Geometries)\tRle\nmix_mline_mpoly_mline--Length(Parts)\tNone\nmix_mline_mpoly_mline--Length(Rings)\tNone\nmix_mline_mpoly_mline--Length(VarBinary)\tNone\nmix_mline_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_mpt_mline--Length(Geometries)\tNone\nmix_mline_mpt_mline--Length(Parts)\tNone\nmix_mline_mpt_mline--Length(VarBinary)\tNone\nmix_mline_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_poly_mline--Length(Geometries)\tRle\nmix_mline_poly_mline--Length(Parts)\tNone\nmix_mline_poly_mline--Length(Rings)\tNone\nmix_mline_poly_mline--Length(VarBinary)\tNone\nmix_mline_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_polyh_mline--Length(Geometries)\tRle\nmix_mline_polyh_mline--Length(Parts)\tNone\nmix_mline_polyh_mline--Length(Rings)\tNone\nmix_mline_polyh_mline--Length(VarBinary)\tNone\nmix_mline_pt_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_pt_mline--Length(Geometries)\tRle\nmix_mline_pt_mline--Length(Parts)\tNone\nmix_mline_pt_mline--Length(VarBinary)\tNone\nmix_mpoly_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_line_mpoly--Length(Geometries)\tRle\nmix_mpoly_line_mpoly--Length(Parts)\tNone\nmix_mpoly_line_mpoly--Length(Rings)\tRle\nmix_mpoly_line_mpoly--Length(VarBinary)\tNone\nmix_mpoly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_mline_mpoly--Length(Geometries)\tRle\nmix_mpoly_mline_mpoly--Length(Parts)\tNone\nmix_mpoly_mline_mpoly--Length(Rings)\tRle\nmix_mpoly_mline_mpoly--Length(VarBinary)\tNone\nmix_mpoly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_mpt_mpoly--Length(Geometries)\tNone\nmix_mpoly_mpt_mpoly--Length(Parts)\tNone\nmix_mpoly_mpt_mpoly--Length(Rings)\tRle\nmix_mpoly_mpt_mpoly--Length(VarBinary)\tNone\nmix_mpoly_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_poly_mpoly--Length(Geometries)\tRle\nmix_mpoly_poly_mpoly--Length(Parts)\tNone\nmix_mpoly_poly_mpoly--Length(Rings)\tRle\nmix_mpoly_poly_mpoly--Length(VarBinary)\tNone\nmix_mpoly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_polyh_mpoly--Length(Geometries)\tRle\nmix_mpoly_polyh_mpoly--Length(Parts)\tNone\nmix_mpoly_polyh_mpoly--Length(Rings)\tRle\nmix_mpoly_polyh_mpoly--Length(VarBinary)\tNone\nmix_mpoly_pt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_pt_mpoly--Length(Geometries)\tRle\nmix_mpoly_pt_mpoly--Length(Parts)\tNone\nmix_mpoly_pt_mpoly--Length(Rings)\tRle\nmix_mpoly_pt_mpoly--Length(VarBinary)\tNone\nmix_mpt_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_line_mpt--Length(Geometries)\tRle\nmix_mpt_line_mpt--Length(Parts)\tNone\nmix_mpt_line_mpt--Length(VarBinary)\tNone\nmix_mpt_mline_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_mline_mpt--Length(Geometries)\tNone\nmix_mpt_mline_mpt--Length(Parts)\tNone\nmix_mpt_mline_mpt--Length(VarBinary)\tNone\nmix_mpt_mpoly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_mpoly_mpt--Length(Geometries)\tNone\nmix_mpt_mpoly_mpt--Length(Parts)\tNone\nmix_mpt_mpoly_mpt--Length(Rings)\tRle\nmix_mpt_mpoly_mpt--Length(VarBinary)\tNone\nmix_mpt_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_poly_mpt--Length(Geometries)\tRle\nmix_mpt_poly_mpt--Length(Parts)\tNone\nmix_mpt_poly_mpt--Length(Rings)\tNone\nmix_mpt_poly_mpt--Length(VarBinary)\tNone\nmix_mpt_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_polyh_mpt--Length(Geometries)\tRle\nmix_mpt_polyh_mpt--Length(Parts)\tNone\nmix_mpt_polyh_mpt--Length(Rings)\tRle\nmix_mpt_polyh_mpt--Length(VarBinary)\tNone\nmix_mpt_pt_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_pt_mpt--Length(Geometries)\tRle\nmix_mpt_pt_mpt--Length(VarBinary)\tNone\nmix_poly_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_line_poly--Length(Parts)\tRle\nmix_poly_line_poly--Length(Rings)\tRle\nmix_poly_line_poly--Length(VarBinary)\tNone\nmix_poly_mline_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mline_poly--Length(Geometries)\tNone\nmix_poly_mline_poly--Length(Parts)\tRle\nmix_poly_mline_poly--Length(Rings)\tNone\nmix_poly_mline_poly--Length(VarBinary)\tNone\nmix_poly_mpoly_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mpoly_poly--Length(Geometries)\tNone\nmix_poly_mpoly_poly--Length(Parts)\tNone\nmix_poly_mpoly_poly--Length(Rings)\tRle\nmix_poly_mpoly_poly--Length(VarBinary)\tNone\nmix_poly_mpt_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mpt_poly--Length(Geometries)\tNone\nmix_poly_mpt_poly--Length(Parts)\tRle\nmix_poly_mpt_poly--Length(Rings)\tRle\nmix_poly_mpt_poly--Length(VarBinary)\tNone\nmix_poly_polyh_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_polyh_poly--Length(Parts)\tNone\nmix_poly_polyh_poly--Length(Rings)\tRle\nmix_poly_polyh_poly--Length(VarBinary)\tRle\nmix_poly_pt_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_pt_poly--Length(Parts)\tRle\nmix_poly_pt_poly--Length(Rings)\tRle\nmix_poly_pt_poly--Length(VarBinary)\tNone\nmix_polyh_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_line_polyh--Length(Parts)\tRle\nmix_polyh_line_polyh--Length(Rings)\tRle\nmix_polyh_line_polyh--Length(VarBinary)\tNone\nmix_polyh_mline_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mline_polyh--Length(Geometries)\tNone\nmix_polyh_mline_polyh--Length(Parts)\tRle\nmix_polyh_mline_polyh--Length(Rings)\tNone\nmix_polyh_mline_polyh--Length(VarBinary)\tNone\nmix_polyh_mpoly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mpoly_polyh--Length(Geometries)\tNone\nmix_polyh_mpoly_polyh--Length(Parts)\tNone\nmix_polyh_mpoly_polyh--Length(Rings)\tRle\nmix_polyh_mpoly_polyh--Length(VarBinary)\tNone\nmix_polyh_mpt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mpt_polyh--Length(Geometries)\tNone\nmix_polyh_mpt_polyh--Length(Parts)\tRle\nmix_polyh_mpt_polyh--Length(Rings)\tRle\nmix_polyh_mpt_polyh--Length(VarBinary)\tNone\nmix_polyh_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_poly_polyh--Length(Parts)\tNone\nmix_polyh_poly_polyh--Length(Rings)\tRle\nmix_polyh_poly_polyh--Length(VarBinary)\tRle\nmix_polyh_pt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_pt_polyh--Length(Parts)\tRle\nmix_polyh_pt_polyh--Length(Rings)\tRle\nmix_polyh_pt_polyh--Length(VarBinary)\tNone\nmix_pt_line_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_line_pt--Length(Parts)\tNone\nmix_pt_line_pt--Length(VarBinary)\tNone\nmix_pt_mline_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mline_pt--Length(Geometries)\tNone\nmix_pt_mline_pt--Length(Parts)\tNone\nmix_pt_mline_pt--Length(VarBinary)\tNone\nmix_pt_mpoly_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mpoly_pt--Length(Geometries)\tNone\nmix_pt_mpoly_pt--Length(Parts)\tNone\nmix_pt_mpoly_pt--Length(Rings)\tRle\nmix_pt_mpoly_pt--Length(VarBinary)\tNone\nmix_pt_mpt_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mpt_pt--Length(Geometries)\tNone\nmix_pt_mpt_pt--Length(VarBinary)\tNone\nmix_pt_poly_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_poly_pt--Length(Parts)\tNone\nmix_pt_poly_pt--Length(Rings)\tNone\nmix_pt_poly_pt--Length(VarBinary)\tNone\nmix_pt_polyh_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_polyh_pt--Length(Parts)\tNone\nmix_pt_polyh_pt--Length(Rings)\tRle\nmix_pt_polyh_pt--Length(VarBinary)\tNone\n"
  },
  {
    "path": "test/synthetic/rust.txt",
    "content": "mix_2_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mline--Length(Geometries)\tNone\nmix_2_line_mline--Length(Parts)\tNone\nmix_2_line_mline--Length(VarBinary)\tNone\nmix_2_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mpoly--Length(Geometries)\tNone\nmix_2_line_mpoly--Length(Parts)\tNone\nmix_2_line_mpoly--Length(Rings)\tNone\nmix_2_line_mpoly--Length(VarBinary)\tNone\nmix_2_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_line_mpt--Length(Geometries)\tNone\nmix_2_line_mpt--Length(Parts)\tNone\nmix_2_line_mpt--Length(VarBinary)\tNone\nmix_2_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_2_line_poly--Length(Parts)\tNone\nmix_2_line_poly--Length(Rings)\tNone\nmix_2_line_poly--Length(VarBinary)\tNone\nmix_2_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_line_polyh--Length(Parts)\tNone\nmix_2_line_polyh--Length(Rings)\tNone\nmix_2_line_polyh--Length(VarBinary)\tNone\nmix_2_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_mline_mpoly--Length(Geometries)\tNone\nmix_2_mline_mpoly--Length(Parts)\tNone\nmix_2_mline_mpoly--Length(Rings)\tNone\nmix_2_mline_mpoly--Length(VarBinary)\tNone\nmix_2_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_mpt_mline--Length(Geometries)\tNone\nmix_2_mpt_mline--Length(Parts)\tNone\nmix_2_mpt_mline--Length(VarBinary)\tNone\nmix_2_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_mpt_mpoly--Length(Geometries)\tNone\nmix_2_mpt_mpoly--Length(Parts)\tNone\nmix_2_mpt_mpoly--Length(Rings)\tNone\nmix_2_mpt_mpoly--Length(VarBinary)\tNone\nmix_2_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mline--Length(Geometries)\tNone\nmix_2_poly_mline--Length(Parts)\tNone\nmix_2_poly_mline--Length(Rings)\tNone\nmix_2_poly_mline--Length(VarBinary)\tNone\nmix_2_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mpoly--Length(Geometries)\tNone\nmix_2_poly_mpoly--Length(Parts)\tNone\nmix_2_poly_mpoly--Length(Rings)\tNone\nmix_2_poly_mpoly--Length(VarBinary)\tNone\nmix_2_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_mpt--Length(Geometries)\tNone\nmix_2_poly_mpt--Length(Parts)\tNone\nmix_2_poly_mpt--Length(Rings)\tNone\nmix_2_poly_mpt--Length(VarBinary)\tNone\nmix_2_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_poly_polyh--Length(Parts)\tNone\nmix_2_poly_polyh--Length(Rings)\tNone\nmix_2_poly_polyh--Length(VarBinary)\tNone\nmix_2_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mline--Length(Geometries)\tNone\nmix_2_polyh_mline--Length(Parts)\tNone\nmix_2_polyh_mline--Length(Rings)\tNone\nmix_2_polyh_mline--Length(VarBinary)\tNone\nmix_2_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mpoly--Length(Geometries)\tNone\nmix_2_polyh_mpoly--Length(Parts)\tNone\nmix_2_polyh_mpoly--Length(Rings)\tNone\nmix_2_polyh_mpoly--Length(VarBinary)\tNone\nmix_2_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_polyh_mpt--Length(Geometries)\tNone\nmix_2_polyh_mpt--Length(Parts)\tNone\nmix_2_polyh_mpt--Length(Rings)\tNone\nmix_2_polyh_mpt--Length(VarBinary)\tNone\nmix_2_pt_line--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_line--Length(Parts)\tNone\nmix_2_pt_line--Length(VarBinary)\tNone\nmix_2_pt_mline--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mline--Length(Geometries)\tNone\nmix_2_pt_mline--Length(Parts)\tNone\nmix_2_pt_mline--Length(VarBinary)\tNone\nmix_2_pt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mpoly--Length(Geometries)\tNone\nmix_2_pt_mpoly--Length(Parts)\tNone\nmix_2_pt_mpoly--Length(Rings)\tNone\nmix_2_pt_mpoly--Length(VarBinary)\tNone\nmix_2_pt_mpt--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_mpt--Length(Geometries)\tNone\nmix_2_pt_mpt--Length(VarBinary)\tNone\nmix_2_pt_poly--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_poly--Length(Parts)\tNone\nmix_2_pt_poly--Length(Rings)\tNone\nmix_2_pt_poly--Length(VarBinary)\tNone\nmix_2_pt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_2_pt_polyh--Length(Parts)\tNone\nmix_2_pt_polyh--Length(Rings)\tNone\nmix_2_pt_polyh--Length(VarBinary)\tNone\nmix_3_line_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mline_mpoly--Length(Geometries)\tNone\nmix_3_line_mline_mpoly--Length(Parts)\tNone\nmix_3_line_mline_mpoly--Length(Rings)\tNone\nmix_3_line_mline_mpoly--Length(VarBinary)\tNone\nmix_3_line_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mpt_mline--Length(Geometries)\tNone\nmix_3_line_mpt_mline--Length(Parts)\tNone\nmix_3_line_mpt_mline--Length(VarBinary)\tNone\nmix_3_line_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_mpt_mpoly--Length(Geometries)\tNone\nmix_3_line_mpt_mpoly--Length(Parts)\tNone\nmix_3_line_mpt_mpoly--Length(Rings)\tNone\nmix_3_line_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_line_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mline--Length(Geometries)\tNone\nmix_3_line_poly_mline--Length(Parts)\tNone\nmix_3_line_poly_mline--Length(Rings)\tNone\nmix_3_line_poly_mline--Length(VarBinary)\tNone\nmix_3_line_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mpoly--Length(Geometries)\tNone\nmix_3_line_poly_mpoly--Length(Parts)\tNone\nmix_3_line_poly_mpoly--Length(Rings)\tNone\nmix_3_line_poly_mpoly--Length(VarBinary)\tNone\nmix_3_line_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_mpt--Length(Geometries)\tNone\nmix_3_line_poly_mpt--Length(Parts)\tNone\nmix_3_line_poly_mpt--Length(Rings)\tNone\nmix_3_line_poly_mpt--Length(VarBinary)\tNone\nmix_3_line_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_line_poly_polyh--Length(Parts)\tNone\nmix_3_line_poly_polyh--Length(Rings)\tNone\nmix_3_line_poly_polyh--Length(VarBinary)\tNone\nmix_3_line_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mline--Length(Geometries)\tNone\nmix_3_line_polyh_mline--Length(Parts)\tNone\nmix_3_line_polyh_mline--Length(Rings)\tNone\nmix_3_line_polyh_mline--Length(VarBinary)\tNone\nmix_3_line_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mpoly--Length(Geometries)\tNone\nmix_3_line_polyh_mpoly--Length(Parts)\tNone\nmix_3_line_polyh_mpoly--Length(Rings)\tNone\nmix_3_line_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_line_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_line_polyh_mpt--Length(Geometries)\tNone\nmix_3_line_polyh_mpt--Length(Parts)\tNone\nmix_3_line_polyh_mpt--Length(Rings)\tNone\nmix_3_line_polyh_mpt--Length(VarBinary)\tNone\nmix_3_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_3_mpt_mline_mpoly--Length(Parts)\tNone\nmix_3_mpt_mline_mpoly--Length(Rings)\tNone\nmix_3_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_3_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mline_mpoly--Length(Geometries)\tNone\nmix_3_poly_mline_mpoly--Length(Parts)\tNone\nmix_3_poly_mline_mpoly--Length(Rings)\tNone\nmix_3_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_3_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mpt_mline--Length(Geometries)\tNone\nmix_3_poly_mpt_mline--Length(Parts)\tNone\nmix_3_poly_mpt_mline--Length(Rings)\tNone\nmix_3_poly_mpt_mline--Length(VarBinary)\tNone\nmix_3_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_3_poly_mpt_mpoly--Length(Parts)\tNone\nmix_3_poly_mpt_mpoly--Length(Rings)\tNone\nmix_3_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mline--Length(Geometries)\tNone\nmix_3_poly_polyh_mline--Length(Parts)\tNone\nmix_3_poly_polyh_mline--Length(Rings)\tNone\nmix_3_poly_polyh_mline--Length(VarBinary)\tNone\nmix_3_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_3_poly_polyh_mpoly--Length(Parts)\tNone\nmix_3_poly_polyh_mpoly--Length(Rings)\tNone\nmix_3_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_poly_polyh_mpt--Length(Geometries)\tNone\nmix_3_poly_polyh_mpt--Length(Parts)\tNone\nmix_3_poly_polyh_mpt--Length(Rings)\tNone\nmix_3_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_3_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_3_polyh_mline_mpoly--Length(Parts)\tNone\nmix_3_polyh_mline_mpoly--Length(Rings)\tNone\nmix_3_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_3_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mpt_mline--Length(Geometries)\tNone\nmix_3_polyh_mpt_mline--Length(Parts)\tNone\nmix_3_polyh_mpt_mline--Length(Rings)\tNone\nmix_3_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_3_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_3_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_3_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_3_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_pt_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mline--Length(Geometries)\tNone\nmix_3_pt_line_mline--Length(Parts)\tNone\nmix_3_pt_line_mline--Length(VarBinary)\tNone\nmix_3_pt_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mpoly--Length(Geometries)\tNone\nmix_3_pt_line_mpoly--Length(Parts)\tNone\nmix_3_pt_line_mpoly--Length(Rings)\tNone\nmix_3_pt_line_mpoly--Length(VarBinary)\tNone\nmix_3_pt_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_mpt--Length(Geometries)\tNone\nmix_3_pt_line_mpt--Length(Parts)\tNone\nmix_3_pt_line_mpt--Length(VarBinary)\tNone\nmix_3_pt_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_poly--Length(Parts)\tNone\nmix_3_pt_line_poly--Length(Rings)\tNone\nmix_3_pt_line_poly--Length(VarBinary)\tNone\nmix_3_pt_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_line_polyh--Length(Parts)\tNone\nmix_3_pt_line_polyh--Length(Rings)\tNone\nmix_3_pt_line_polyh--Length(VarBinary)\tNone\nmix_3_pt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mline_mpoly--Length(Geometries)\tNone\nmix_3_pt_mline_mpoly--Length(Parts)\tNone\nmix_3_pt_mline_mpoly--Length(Rings)\tNone\nmix_3_pt_mline_mpoly--Length(VarBinary)\tNone\nmix_3_pt_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mpt_mline--Length(Geometries)\tNone\nmix_3_pt_mpt_mline--Length(Parts)\tNone\nmix_3_pt_mpt_mline--Length(VarBinary)\tNone\nmix_3_pt_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_mpt_mpoly--Length(Geometries)\tNone\nmix_3_pt_mpt_mpoly--Length(Parts)\tNone\nmix_3_pt_mpt_mpoly--Length(Rings)\tNone\nmix_3_pt_mpt_mpoly--Length(VarBinary)\tNone\nmix_3_pt_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mline--Length(Geometries)\tNone\nmix_3_pt_poly_mline--Length(Parts)\tNone\nmix_3_pt_poly_mline--Length(Rings)\tNone\nmix_3_pt_poly_mline--Length(VarBinary)\tNone\nmix_3_pt_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mpoly--Length(Geometries)\tNone\nmix_3_pt_poly_mpoly--Length(Parts)\tNone\nmix_3_pt_poly_mpoly--Length(Rings)\tNone\nmix_3_pt_poly_mpoly--Length(VarBinary)\tNone\nmix_3_pt_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_mpt--Length(Geometries)\tNone\nmix_3_pt_poly_mpt--Length(Parts)\tNone\nmix_3_pt_poly_mpt--Length(Rings)\tNone\nmix_3_pt_poly_mpt--Length(VarBinary)\tNone\nmix_3_pt_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_poly_polyh--Length(Parts)\tNone\nmix_3_pt_poly_polyh--Length(Rings)\tNone\nmix_3_pt_poly_polyh--Length(VarBinary)\tNone\nmix_3_pt_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mline--Length(Geometries)\tNone\nmix_3_pt_polyh_mline--Length(Parts)\tNone\nmix_3_pt_polyh_mline--Length(Rings)\tNone\nmix_3_pt_polyh_mline--Length(VarBinary)\tNone\nmix_3_pt_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mpoly--Length(Geometries)\tNone\nmix_3_pt_polyh_mpoly--Length(Parts)\tNone\nmix_3_pt_polyh_mpoly--Length(Rings)\tNone\nmix_3_pt_polyh_mpoly--Length(VarBinary)\tNone\nmix_3_pt_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_3_pt_polyh_mpt--Length(Geometries)\tNone\nmix_3_pt_polyh_mpt--Length(Parts)\tNone\nmix_3_pt_polyh_mpt--Length(Rings)\tNone\nmix_3_pt_polyh_mpt--Length(VarBinary)\tNone\nmix_4_line_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_line_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_line_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_line_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mline_mpoly--Length(Geometries)\tNone\nmix_4_line_poly_mline_mpoly--Length(Parts)\tNone\nmix_4_line_poly_mline_mpoly--Length(Rings)\tNone\nmix_4_line_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mpt_mline--Length(Geometries)\tNone\nmix_4_line_poly_mpt_mline--Length(Parts)\tNone\nmix_4_line_poly_mpt_mline--Length(Rings)\tNone\nmix_4_line_poly_mpt_mline--Length(VarBinary)\tNone\nmix_4_line_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_4_line_poly_mpt_mpoly--Length(Parts)\tNone\nmix_4_line_poly_mpt_mpoly--Length(Rings)\tNone\nmix_4_line_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mline--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mline--Length(Parts)\tNone\nmix_4_line_poly_polyh_mline--Length(Rings)\tNone\nmix_4_line_poly_polyh_mline--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mpoly--Length(Parts)\tNone\nmix_4_line_poly_polyh_mpoly--Length(Rings)\tNone\nmix_4_line_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_line_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_line_poly_polyh_mpt--Length(Geometries)\tNone\nmix_4_line_poly_polyh_mpt--Length(Parts)\tNone\nmix_4_line_poly_polyh_mpt--Length(Rings)\tNone\nmix_4_line_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_4_line_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_4_line_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_line_polyh_mline_mpoly--Length(Rings)\tNone\nmix_4_line_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_line_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_line_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_line_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_line_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_4_line_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_line_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_line_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_line_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_4_line_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_4_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_poly_polyh_mline_mpoly--Length(Rings)\tNone\nmix_4_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_4_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_poly_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_4_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mline_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_line_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_line_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_line_mpt_mline--Length(Parts)\tNone\nmix_4_pt_line_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_line_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_line_mpt_mpoly--Length(Rings)\tNone\nmix_4_pt_line_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mline--Length(Geometries)\tNone\nmix_4_pt_line_poly_mline--Length(Parts)\tNone\nmix_4_pt_line_poly_mline--Length(Rings)\tNone\nmix_4_pt_line_poly_mline--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_poly_mpoly--Length(Parts)\tNone\nmix_4_pt_line_poly_mpoly--Length(Rings)\tNone\nmix_4_pt_line_poly_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_mpt--Length(Geometries)\tNone\nmix_4_pt_line_poly_mpt--Length(Parts)\tNone\nmix_4_pt_line_poly_mpt--Length(Rings)\tNone\nmix_4_pt_line_poly_mpt--Length(VarBinary)\tNone\nmix_4_pt_line_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_poly_polyh--Length(Parts)\tNone\nmix_4_pt_line_poly_polyh--Length(Rings)\tNone\nmix_4_pt_line_poly_polyh--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mline--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mline--Length(Parts)\tNone\nmix_4_pt_line_polyh_mline--Length(Rings)\tNone\nmix_4_pt_line_polyh_mline--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mpoly--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mpoly--Length(Parts)\tNone\nmix_4_pt_line_polyh_mpoly--Length(Rings)\tNone\nmix_4_pt_line_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_pt_line_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_line_polyh_mpt--Length(Geometries)\tNone\nmix_4_pt_line_polyh_mpt--Length(Parts)\tNone\nmix_4_pt_line_polyh_mpt--Length(Rings)\tNone\nmix_4_pt_line_polyh_mpt--Length(VarBinary)\tNone\nmix_4_pt_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_4_pt_mpt_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_mpt_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mline_mpoly--Length(Geometries)\tNone\nmix_4_pt_poly_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_poly_mpt_mline--Length(Parts)\tNone\nmix_4_pt_poly_mpt_mline--Length(Rings)\tNone\nmix_4_pt_poly_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_poly_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_mpt_mpoly--Length(Rings)\tNone\nmix_4_pt_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mline--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mline--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mline--Length(Rings)\tNone\nmix_4_pt_poly_polyh_mline--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mpoly--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mpoly--Length(Rings)\tNone\nmix_4_pt_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_4_pt_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_poly_polyh_mpt--Length(Geometries)\tNone\nmix_4_pt_poly_polyh_mpt--Length(Parts)\tNone\nmix_4_pt_poly_polyh_mpt--Length(Rings)\tNone\nmix_4_pt_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_4_pt_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_4_pt_polyh_mline_mpoly--Length(Parts)\tNone\nmix_4_pt_polyh_mline_mpoly--Length(Rings)\tNone\nmix_4_pt_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_4_pt_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mpt_mline--Length(Geometries)\tNone\nmix_4_pt_polyh_mpt_mline--Length(Parts)\tNone\nmix_4_pt_polyh_mpt_mline--Length(Rings)\tNone\nmix_4_pt_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_4_pt_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_4_pt_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_4_pt_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_4_pt_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_4_pt_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_line_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_line_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_5_line_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_line_poly_polyh_mline_mpoly--Length(Rings)\tNone\nmix_5_line_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_line_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_line_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_5_line_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_line_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_line_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_line_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_poly_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_line_poly_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(Parts)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(Rings)\tNone\nmix_5_pt_line_poly_mpt_mline--Length(VarBinary)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Length(Rings)\tNone\nmix_5_pt_line_poly_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mline--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(Rings)\tNone\nmix_5_pt_line_poly_polyh_mline--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Length(Rings)\tNone\nmix_5_pt_line_poly_polyh_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_poly_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_poly_polyh_mpt--Length(Geometries)\tNone\nmix_5_pt_line_poly_polyh_mpt--Length(Parts)\tNone\nmix_5_pt_line_poly_polyh_mpt--Length(Rings)\tNone\nmix_5_pt_line_poly_polyh_mpt--Length(VarBinary)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_line_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_line_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_pt_line_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_line_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_5_pt_line_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_5_pt_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_5_pt_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_5_pt_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_6_line_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(Rings)\tNone\nmix_6_pt_line_poly_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_poly_polyh_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(Rings)\tNone\nmix_6_pt_line_poly_polyh_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Geometries)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(Rings)\tNone\nmix_6_pt_line_poly_polyh_mpt_mline--Length(VarBinary)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Parts)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(Rings)\tNone\nmix_6_pt_line_poly_polyh_mpt_mpoly--Length(VarBinary)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_6_pt_line_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_6_pt_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Geometries)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Parts)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(Rings)\tNone\nmix_7_pt_line_poly_polyh_mpt_mline_mpoly--Length(VarBinary)\tNone\nmix_dup_line--Data(Vertex)\tComponentwiseDelta\nmix_dup_line--Length(Parts)\tNone\nmix_dup_line--Length(VarBinary)\tNone\nmix_dup_mline--Data(Vertex)\tComponentwiseDelta\nmix_dup_mline--Length(Geometries)\tNone\nmix_dup_mline--Length(Parts)\tNone\nmix_dup_mline--Length(VarBinary)\tNone\nmix_dup_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_dup_mpoly--Length(Geometries)\tNone\nmix_dup_mpoly--Length(Parts)\tNone\nmix_dup_mpoly--Length(Rings)\tNone\nmix_dup_mpoly--Length(VarBinary)\tNone\nmix_dup_mpt--Data(Vertex)\tComponentwiseDelta\nmix_dup_mpt--Length(Geometries)\tNone\nmix_dup_mpt--Length(VarBinary)\tNone\nmix_dup_poly--Data(Vertex)\tComponentwiseDelta\nmix_dup_poly--Length(Parts)\tNone\nmix_dup_poly--Length(Rings)\tNone\nmix_dup_poly--Length(VarBinary)\tNone\nmix_dup_polyh--Data(Vertex)\tComponentwiseDelta\nmix_dup_polyh--Length(Parts)\tNone\nmix_dup_polyh--Length(Rings)\tNone\nmix_dup_polyh--Length(VarBinary)\tNone\nmix_dup_pt--Data(Vertex)\tComponentwiseDelta\nmix_dup_pt--Length(VarBinary)\tNone\nmix_line_mline_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mline_line--Length(Geometries)\tNone\nmix_line_mline_line--Length(Parts)\tNone\nmix_line_mline_line--Length(VarBinary)\tNone\nmix_line_mpoly_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mpoly_line--Length(Geometries)\tNone\nmix_line_mpoly_line--Length(Parts)\tNone\nmix_line_mpoly_line--Length(Rings)\tNone\nmix_line_mpoly_line--Length(VarBinary)\tNone\nmix_line_mpt_line--Data(Vertex)\tComponentwiseDelta\nmix_line_mpt_line--Length(Geometries)\tNone\nmix_line_mpt_line--Length(Parts)\tNone\nmix_line_mpt_line--Length(VarBinary)\tNone\nmix_line_poly_line--Data(Vertex)\tComponentwiseDelta\nmix_line_poly_line--Length(Parts)\tNone\nmix_line_poly_line--Length(Rings)\tNone\nmix_line_poly_line--Length(VarBinary)\tNone\nmix_line_polyh_line--Data(Vertex)\tComponentwiseDelta\nmix_line_polyh_line--Length(Parts)\tNone\nmix_line_polyh_line--Length(Rings)\tNone\nmix_line_polyh_line--Length(VarBinary)\tNone\nmix_line_pt_line--Data(Vertex)\tComponentwiseDelta\nmix_line_pt_line--Length(Parts)\tNone\nmix_line_pt_line--Length(VarBinary)\tNone\nmix_mline_line_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_line_mline--Length(Geometries)\tNone\nmix_mline_line_mline--Length(Parts)\tNone\nmix_mline_line_mline--Length(VarBinary)\tNone\nmix_mline_mpoly_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_mpoly_mline--Length(Geometries)\tNone\nmix_mline_mpoly_mline--Length(Parts)\tNone\nmix_mline_mpoly_mline--Length(Rings)\tNone\nmix_mline_mpoly_mline--Length(VarBinary)\tNone\nmix_mline_mpt_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_mpt_mline--Length(Geometries)\tNone\nmix_mline_mpt_mline--Length(Parts)\tNone\nmix_mline_mpt_mline--Length(VarBinary)\tNone\nmix_mline_poly_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_poly_mline--Length(Geometries)\tNone\nmix_mline_poly_mline--Length(Parts)\tNone\nmix_mline_poly_mline--Length(Rings)\tNone\nmix_mline_poly_mline--Length(VarBinary)\tNone\nmix_mline_polyh_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_polyh_mline--Length(Geometries)\tNone\nmix_mline_polyh_mline--Length(Parts)\tNone\nmix_mline_polyh_mline--Length(Rings)\tNone\nmix_mline_polyh_mline--Length(VarBinary)\tNone\nmix_mline_pt_mline--Data(Vertex)\tComponentwiseDelta\nmix_mline_pt_mline--Length(Geometries)\tNone\nmix_mline_pt_mline--Length(Parts)\tNone\nmix_mline_pt_mline--Length(VarBinary)\tNone\nmix_mpoly_line_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_line_mpoly--Length(Geometries)\tNone\nmix_mpoly_line_mpoly--Length(Parts)\tNone\nmix_mpoly_line_mpoly--Length(Rings)\tNone\nmix_mpoly_line_mpoly--Length(VarBinary)\tNone\nmix_mpoly_mline_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_mline_mpoly--Length(Geometries)\tNone\nmix_mpoly_mline_mpoly--Length(Parts)\tNone\nmix_mpoly_mline_mpoly--Length(Rings)\tNone\nmix_mpoly_mline_mpoly--Length(VarBinary)\tNone\nmix_mpoly_mpt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_mpt_mpoly--Length(Geometries)\tNone\nmix_mpoly_mpt_mpoly--Length(Parts)\tNone\nmix_mpoly_mpt_mpoly--Length(Rings)\tNone\nmix_mpoly_mpt_mpoly--Length(VarBinary)\tNone\nmix_mpoly_poly_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_poly_mpoly--Length(Geometries)\tNone\nmix_mpoly_poly_mpoly--Length(Parts)\tNone\nmix_mpoly_poly_mpoly--Length(Rings)\tNone\nmix_mpoly_poly_mpoly--Length(VarBinary)\tNone\nmix_mpoly_polyh_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_polyh_mpoly--Length(Geometries)\tNone\nmix_mpoly_polyh_mpoly--Length(Parts)\tNone\nmix_mpoly_polyh_mpoly--Length(Rings)\tNone\nmix_mpoly_polyh_mpoly--Length(VarBinary)\tNone\nmix_mpoly_pt_mpoly--Data(Vertex)\tComponentwiseDelta\nmix_mpoly_pt_mpoly--Length(Geometries)\tNone\nmix_mpoly_pt_mpoly--Length(Parts)\tNone\nmix_mpoly_pt_mpoly--Length(Rings)\tNone\nmix_mpoly_pt_mpoly--Length(VarBinary)\tNone\nmix_mpt_line_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_line_mpt--Length(Geometries)\tNone\nmix_mpt_line_mpt--Length(Parts)\tNone\nmix_mpt_line_mpt--Length(VarBinary)\tNone\nmix_mpt_mline_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_mline_mpt--Length(Geometries)\tNone\nmix_mpt_mline_mpt--Length(Parts)\tNone\nmix_mpt_mline_mpt--Length(VarBinary)\tNone\nmix_mpt_mpoly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_mpoly_mpt--Length(Geometries)\tNone\nmix_mpt_mpoly_mpt--Length(Parts)\tNone\nmix_mpt_mpoly_mpt--Length(Rings)\tNone\nmix_mpt_mpoly_mpt--Length(VarBinary)\tNone\nmix_mpt_poly_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_poly_mpt--Length(Geometries)\tNone\nmix_mpt_poly_mpt--Length(Parts)\tNone\nmix_mpt_poly_mpt--Length(Rings)\tNone\nmix_mpt_poly_mpt--Length(VarBinary)\tNone\nmix_mpt_polyh_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_polyh_mpt--Length(Geometries)\tNone\nmix_mpt_polyh_mpt--Length(Parts)\tNone\nmix_mpt_polyh_mpt--Length(Rings)\tNone\nmix_mpt_polyh_mpt--Length(VarBinary)\tNone\nmix_mpt_pt_mpt--Data(Vertex)\tComponentwiseDelta\nmix_mpt_pt_mpt--Length(Geometries)\tNone\nmix_mpt_pt_mpt--Length(VarBinary)\tNone\nmix_poly_line_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_line_poly--Length(Parts)\tNone\nmix_poly_line_poly--Length(Rings)\tNone\nmix_poly_line_poly--Length(VarBinary)\tNone\nmix_poly_mline_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mline_poly--Length(Geometries)\tNone\nmix_poly_mline_poly--Length(Parts)\tNone\nmix_poly_mline_poly--Length(Rings)\tNone\nmix_poly_mline_poly--Length(VarBinary)\tNone\nmix_poly_mpoly_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mpoly_poly--Length(Geometries)\tNone\nmix_poly_mpoly_poly--Length(Parts)\tNone\nmix_poly_mpoly_poly--Length(Rings)\tNone\nmix_poly_mpoly_poly--Length(VarBinary)\tNone\nmix_poly_mpt_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_mpt_poly--Length(Geometries)\tNone\nmix_poly_mpt_poly--Length(Parts)\tNone\nmix_poly_mpt_poly--Length(Rings)\tNone\nmix_poly_mpt_poly--Length(VarBinary)\tNone\nmix_poly_polyh_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_polyh_poly--Length(Parts)\tNone\nmix_poly_polyh_poly--Length(Rings)\tNone\nmix_poly_polyh_poly--Length(VarBinary)\tNone\nmix_poly_pt_poly--Data(Vertex)\tComponentwiseDelta\nmix_poly_pt_poly--Length(Parts)\tNone\nmix_poly_pt_poly--Length(Rings)\tNone\nmix_poly_pt_poly--Length(VarBinary)\tNone\nmix_polyh_line_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_line_polyh--Length(Parts)\tNone\nmix_polyh_line_polyh--Length(Rings)\tNone\nmix_polyh_line_polyh--Length(VarBinary)\tNone\nmix_polyh_mline_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mline_polyh--Length(Geometries)\tNone\nmix_polyh_mline_polyh--Length(Parts)\tNone\nmix_polyh_mline_polyh--Length(Rings)\tNone\nmix_polyh_mline_polyh--Length(VarBinary)\tNone\nmix_polyh_mpoly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mpoly_polyh--Length(Geometries)\tNone\nmix_polyh_mpoly_polyh--Length(Parts)\tNone\nmix_polyh_mpoly_polyh--Length(Rings)\tNone\nmix_polyh_mpoly_polyh--Length(VarBinary)\tNone\nmix_polyh_mpt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_mpt_polyh--Length(Geometries)\tNone\nmix_polyh_mpt_polyh--Length(Parts)\tNone\nmix_polyh_mpt_polyh--Length(Rings)\tNone\nmix_polyh_mpt_polyh--Length(VarBinary)\tNone\nmix_polyh_poly_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_poly_polyh--Length(Parts)\tNone\nmix_polyh_poly_polyh--Length(Rings)\tNone\nmix_polyh_poly_polyh--Length(VarBinary)\tNone\nmix_polyh_pt_polyh--Data(Vertex)\tComponentwiseDelta\nmix_polyh_pt_polyh--Length(Parts)\tNone\nmix_polyh_pt_polyh--Length(Rings)\tNone\nmix_polyh_pt_polyh--Length(VarBinary)\tNone\nmix_pt_line_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_line_pt--Length(Parts)\tNone\nmix_pt_line_pt--Length(VarBinary)\tNone\nmix_pt_mline_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mline_pt--Length(Geometries)\tNone\nmix_pt_mline_pt--Length(Parts)\tNone\nmix_pt_mline_pt--Length(VarBinary)\tNone\nmix_pt_mpoly_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mpoly_pt--Length(Geometries)\tNone\nmix_pt_mpoly_pt--Length(Parts)\tNone\nmix_pt_mpoly_pt--Length(Rings)\tNone\nmix_pt_mpoly_pt--Length(VarBinary)\tNone\nmix_pt_mpt_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_mpt_pt--Length(Geometries)\tNone\nmix_pt_mpt_pt--Length(VarBinary)\tNone\nmix_pt_poly_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_poly_pt--Length(Parts)\tNone\nmix_pt_poly_pt--Length(Rings)\tNone\nmix_pt_poly_pt--Length(VarBinary)\tNone\nmix_pt_polyh_pt--Data(Vertex)\tComponentwiseDelta\nmix_pt_polyh_pt--Length(Parts)\tNone\nmix_pt_polyh_pt--Length(Rings)\tNone\nmix_pt_polyh_pt--Length(VarBinary)\tNone\n"
  },
  {
    "path": "test/synthetic/synthetic-test-utils/index.ts",
    "content": "import { globSync, readFileSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst RELATIVE_FLOAT_TOLERANCE = 0.0001 / 100;\nconst ABSOLUTE_FLOAT_TOLERANCE = Number.EPSILON;\n\nexport function compareWithTolerance(\n  received: unknown,\n  expected: unknown,\n): boolean | undefined {\n  if (typeof expected === \"string\") {\n    if (expected.endsWith(\"NAN\")) {\n      expected = Number.NaN;\n    } else if (expected.endsWith(\"INFINITY\")) {\n      expected = expected.endsWith(\"NEG_INFINITY\")\n        ? Number.NEGATIVE_INFINITY\n        : Number.POSITIVE_INFINITY;\n    }\n  }\n\n  if (typeof received !== \"number\" || typeof expected !== \"number\") {\n    return undefined;\n  }\n\n  if (!Number.isFinite(expected)) return Object.is(received, expected);\n\n  if (Math.abs(expected) < ABSOLUTE_FLOAT_TOLERANCE) {\n    return Math.abs(received) <= ABSOLUTE_FLOAT_TOLERANCE;\n  }\n\n  const relativeError = Math.abs(received - expected) / Math.abs(expected);\n  return relativeError <= RELATIVE_FLOAT_TOLERANCE;\n}\n\nexport function writeActualOutput(\n  mltFile: string,\n  actual: Record<string, unknown>,\n): string {\n  const actualFile = mltFile.replace(/\\.mlt$/, \".actual.json\");\n  writeFileSync(actualFile, `${JSON.stringify(actual, null, 2)}\\n`, \"utf-8\");\n  return actualFile;\n}\n\nexport function getTestCases(skipList: string[]): {\n  active: { name: string; content: object; fileName: string }[];\n  skipped: string[];\n} {\n  const syntheticDir = resolve(__dirname, \"..\");\n  const mltFiles = globSync(`**/*.mlt`, {\n    cwd: syntheticDir,\n  }).map((mltFile: string) => path.join(syntheticDir, mltFile));\n\n  const active: { name: string; content: object; fileName: string }[] = [];\n  const skipped: string[] = [];\n\n  for (const mltFile of mltFiles) {\n    const testName = path.relative(syntheticDir, mltFile).replace(/\\.mlt$/, \"\");\n    if (skipList.includes(testName)) {\n      skipped.push(testName);\n    } else {\n      const jsonFile = mltFile.replace(/\\.mlt$/, \".json\");\n      const expectedRaw = readFileSync(jsonFile, \"utf-8\");\n      const expected = JSON.parse(expectedRaw);\n      active.push({ name: testName, fileName: mltFile, content: expected });\n    }\n  }\n\n  return { active, skipped };\n}\n"
  },
  {
    "path": "test/synthetic/synthetic-test-utils/package.json",
    "content": "{\n  \"name\": \"synthetic-test-utils\",\n  \"main\": \"index.ts\",\n  \"type\": \"module\",\n  \"dependencies\": { }\n}\n"
  },
  {
    "path": "ts/.gitignore",
    "content": "dist/\nLICENSE.txt\n"
  },
  {
    "path": "ts/.nvmrc",
    "content": "24.11\n"
  },
  {
    "path": "ts/README.md",
    "content": "# maplibre-tile-spec\n\nThis package contains a JavaScript decoder for the experimental MapLibre Tile (MLT) vector tile format.\n\n## Install\n\n`npm install @maplibre/maplibre-tile-spec`\n\n## Quickstart\n\nTo decode a tile, you will want to load `MltDecoder`:\n\n```js\nimport { decodeTile } from '@maplibre/maplibre-tile-spec';\n\nconst data = fs.readFileSync(tilePath);\nconst tile = decodeTile(data);\n```\n## Contents\n\n### Code\n\nCode is in `src/`.\n\n### Tests\n\nTests are in `test/unit/`. Run tests by running `npm run test`.\ncurrently not integrated\n"
  },
  {
    "path": "ts/RELEASE.md",
    "content": "# Release to NPM\n\n1. Run the `js-bump-version.yml` workflow. Select the appropiate type of version bump.\n2. Merge the PR this workflow creates.\n\nThe `release-js.yml` workflow will now create a release.\n"
  },
  {
    "path": "ts/biome.json",
    "content": "{\n  \"root\": false,\n  \"$schema\": \"https://biomejs.dev/schemas/2.0.5/schema.json\",\n  \"javascript\": {\n    \"formatter\": {\n      \"indentWidth\": 4,\n      \"indentStyle\": \"space\",\n      \"lineWidth\": 120\n    }\n  },\n  \"linter\": {\n    \"rules\": {\n      \"suspicious\": {\n        \"noExplicitAny\": \"off\",\n        \"noImplicitAnyLet\": \"off\",\n        \"noShadowRestrictedNames\": \"off\"\n      },\n      \"style\": {\n        \"noParameterAssign\": \"off\"\n      }\n    }\n  },\n  \"assist\": {\n    \"actions\": {\n      \"source\": {\n        \"organizeImports\": \"off\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ts/buf.gen.yaml",
    "content": "version: v1\nplugins:\n   - plugin: es\n     opt: target=ts\n     out: src/metadata/tileset\n"
  },
  {
    "path": "ts/eslint.config.mjs",
    "content": "import eslint from \"@eslint/js\";\nimport tseslint from \"typescript-eslint\";\n\nexport default tseslint.config(\n    eslint.configs.recommended,\n    ...tseslint.configs.recommended,\n    ...tseslint.configs.recommendedTypeChecked,\n    ...tseslint.configs.strict,\n    {\n        languageOptions: {\n            parserOptions: {\n                project: \"./tsconfig.lint.json\",\n                tsconfigRootDir: import.meta.dirname,\n            },\n        },\n        rules: {\n            // All the below are disabled, but they need to be enabled at some point...\n            \"@typescript-eslint/no-explicit-any\": \"off\",\n            \"@typescript-eslint/no-unsafe-argument\": \"off\",\n            \"@typescript-eslint/no-unsafe-call\": \"off\",\n            \"@typescript-eslint/no-unsafe-member-access\": \"off\",\n            \"@typescript-eslint/no-unsafe-return\": \"off\",\n            \"@typescript-eslint/no-unsafe-assignment\": \"off\",\n            \"@typescript-eslint/no-extraneous-class\": \"off\",\n            \"@typescript-eslint/prefer-literal-enum-member\": \"off\",\n            \"@typescript-eslint/restrict-template-expressions\": \"off\",\n            \"no-case-declarations\": \"off\",\n            \"@typescript-eslint/no-base-to-string\": \"off\",\n            \"@typescript-eslint/no-unsafe-enum-comparison\": \"off\",\n            \"@typescript-eslint/no-dynamic-delete\": \"off\",\n            \"@typescript-eslint/no-unused-vars\": \"off\",\n            //'@typescript-eslint/no-unused-vars': ['error', {\n            //  argsIgnorePattern: '^_',\n            //  varsIgnorePattern: '^_'\n            //}],\n            \"@typescript-eslint/consistent-type-imports\": [\n                \"error\",\n                {\n                    prefer: \"type-imports\",\n                    fixStyle: \"inline-type-imports\",\n                },\n            ],\n        },\n    },\n    {\n        files: [\"**/*.spec.ts\"],\n        rules: {\n            \"@typescript-eslint/unbound-method\": \"off\",\n        },\n    },\n    {\n        ignores: [\"dist\", \"node_modules\", \"coverage\", \"*.config.mjs\", \"*.config.js\", \"**/*.js\"],\n    },\n);\n"
  },
  {
    "path": "ts/mod.just",
    "content": "just := quote(just_executable())\n\n_default: (just '--list' 'ts')\n\n[private]\njust *args:\n    {{just}} {{args}}\n\n# Build the package\nbuild: install\n    npm run build\n\n# Quick compile/type check\ncheck: install\n    npm run build\n\n# Run all CI steps: lint, test, build\nci-test: lint test build\n    {{just}} assert-git-is-clean\n\n# Delete build artifacts\nclean:\n    rm -rf node_modules dist\n\n# Reformat code\nfmt: install\n    npm run format\n\n# Install dependencies\ninstall:\n    npm ci\n\n# Run linting\nlint: install\n    npm run lint\n\n# Run tests\ntest: install\n    npm run test\n\n# Run benchmark\nbench: install\n    echo \"TODO: Add js benchmark command\"\n"
  },
  {
    "path": "ts/package.json",
    "content": "{\n  \"name\": \"@maplibre/mlt\",\n  \"version\": \"1.1.9\",\n  \"main\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"files\": [\n    \"/dist\"\n  ],\n  \"license\": \"(MIT OR Apache-2.0)\",\n  \"homepage\": \"https://github.com/maplibre/maplibre-tile-spec/#readme\",\n  \"keywords\": [\n    \"maplibre\",\n    \"gis\",\n    \"vector\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc\",\n    \"test\": \"vitest run --coverage --coverage.reportOnFailure\",\n    \"lint\": \"eslint\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/maplibre/maplibre-tile-spec\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/maplibre/maplibre-tile-spec/issues\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^10.0.1\",\n    \"@mapbox/vector-tile\": \"^2.0.4\",\n    \"@maplibre/maplibre-gl-style-spec\": \"^24.7.0\",\n    \"@types/bytebuffer\": \"^5.0.49\",\n    \"@types/earcut\": \"^3.0.0\",\n    \"@types/node\": \"^25.5.0\",\n    \"@types/pbf\": \"^3.0.5\",\n    \"@vitest/coverage-v8\": \"^4.1.0\",\n    \"bytebuffer\": \"^5.0.1\",\n    \"earcut\": \"^3.0.2\",\n    \"eslint\": \"^10.0.3\",\n    \"pbf\": \"^4.0.1\",\n    \"typescript\": \"^5.9.3\",\n    \"typescript-eslint\": \"^8.57.1\",\n    \"vitest\": \"^4.0.1\"\n  },\n  \"dependencies\": {\n    \"@mapbox/point-geometry\": \"^1.1.0\"\n  }\n}\n"
  },
  {
    "path": "ts/src/decoding/bigEndianDecode.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { decodeBigEndianInt32sInto } from \"./bigEndianDecode\";\nimport { encodeBigEndianInt32s } from \"../encoding/bigEndianEncode\";\n\nfunction decodeThenEncode(bytes: Uint8Array, offset: number, byteLength: number): Uint8Array {\n    const out = new Int32Array(Math.ceil(byteLength / 4));\n    const written = decodeBigEndianInt32sInto(bytes, offset, byteLength, out);\n    return encodeBigEndianInt32s(out.subarray(0, written));\n}\n\nfunction expectDecodeEncodeRoundTrip(bytes: Uint8Array, offset: number, byteLength: number): Uint8Array {\n    const encoded = decodeThenEncode(bytes, offset, byteLength);\n    const expected = bytes.subarray(offset, offset + byteLength);\n\n    expect(encoded.subarray(0, byteLength)).toEqual(expected);\n    for (let i = byteLength; i < encoded.length; i++) expect(encoded[i]).toBe(0);\n    expect(encoded.length).toBe(Math.ceil(byteLength / 4) * 4);\n\n    return encoded;\n}\n\ndescribe(\"decodeBigEndianInt32s\", () => {\n    it(\"round-trips bytes (aligned offset)\", () => {\n        const bytes = new Uint8Array([0x12, 0x34, 0x56, 0x78, 0xff, 0xff, 0xff, 0xff]);\n        expectDecodeEncodeRoundTrip(bytes, 0, bytes.length);\n    });\n\n    it(\"round-trips bytes (unaligned offset)\", () => {\n        const buffer = new Uint8Array(10);\n        buffer[1] = 0x00;\n        buffer[2] = 0x00;\n        buffer[3] = 0x00;\n        buffer[4] = 0x42;\n        expectDecodeEncodeRoundTrip(buffer, 1, 4);\n    });\n\n    it(\"round-trips bytes with trailing bytes (length not multiple of 4)\", () => {\n        const bytes = new Uint8Array([0x00, 0x00, 0x01, 0x00, 0xab]);\n        expectDecodeEncodeRoundTrip(bytes, 0, 5);\n    });\n\n    it(\"round-trips bytes with 1-3 trailing bytes\", () => {\n        expectDecodeEncodeRoundTrip(new Uint8Array([0xaa]), 0, 1);\n        expectDecodeEncodeRoundTrip(new Uint8Array([0xaa, 0xbb]), 0, 2);\n        expectDecodeEncodeRoundTrip(new Uint8Array([0xaa, 0xbb, 0xcc]), 0, 3);\n    });\n\n    it(\"throws on out of bounds\", () => {\n        const bytes = new Uint8Array(4);\n        expect(() => decodeBigEndianInt32sInto(bytes, 0, 5, new Int32Array(2))).toThrow();\n        expect(() => decodeBigEndianInt32sInto(bytes, -1, 4, new Int32Array(1))).toThrow();\n    });\n\n    it(\"round-trips with encodeBigEndianInt32s (aligned)\", () => {\n        const input = new Int32Array([0, 1, -1, 0x12345678]);\n        const bytes = encodeBigEndianInt32s(input);\n\n        const out = new Int32Array(input.length);\n        const written = decodeBigEndianInt32sInto(bytes, 0, bytes.length, out);\n        expect(written).toBe(input.length);\n        expect(out).toEqual(input);\n    });\n\n    it(\"round-trips with encodeBigEndianInt32s (unaligned view)\", () => {\n        const input = new Int32Array([0x01020304, -123456789, 0, 42]);\n        const bytes = encodeBigEndianInt32s(input);\n\n        const buffer = new Uint8Array(bytes.length + 3);\n        buffer.set([0xaa, 0xbb, 0xcc], 0);\n        buffer.set(bytes, 3);\n\n        const out = new Int32Array(input.length);\n        const written = decodeBigEndianInt32sInto(buffer, 3, bytes.length, out);\n        expect(written).toBe(input.length);\n        expect(out).toEqual(input);\n    });\n\n    it(\"decodes into a provided buffer\", () => {\n        const input = new Int32Array([0x01020304, -123456789, 0, 42]);\n        const bytes = encodeBigEndianInt32s(input);\n\n        const out = new Int32Array(input.length + 8);\n        const written = decodeBigEndianInt32sInto(bytes, 0, bytes.length, out);\n        expect(written).toBe(input.length);\n\n        const encoded = encodeBigEndianInt32s(out.subarray(0, written));\n        expect(encoded).toEqual(bytes);\n    });\n\n    it(\"decodeBigEndianInt32sInto throws when output buffer is too small\", () => {\n        const bytes = new Uint8Array([0x12, 0x34, 0x56, 0x78]);\n        const out = new Int32Array(0);\n        expect(() => decodeBigEndianInt32sInto(bytes, 0, bytes.length, out)).toThrow();\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/bigEndianDecode.ts",
    "content": "import { bswap32 } from \"./fastPforShared\";\n\n/**\n * Decodes big-endian bytes into `out` without allocating the output buffer.\n *\n * This function does not copy `bytes`; it writes decoded words into the provided `out` array.\n * For aligned inputs it may create a temporary typed-array view (`Uint32Array`) over `bytes.buffer`\n * to speed up decoding.\n *\n * If `byteLength` is not a multiple of 4, the final word is padded with zeros.\n *\n * @returns Number of int32 words written.\n * @throws RangeError If `(offset, byteLength)` is out of bounds, or if `out` is too small.\n */\nexport function decodeBigEndianInt32sInto(\n    bytes: Uint8Array,\n    offset: number,\n    byteLength: number,\n    out: Uint32Array,\n): number {\n    if (offset < 0 || byteLength < 0 || offset + byteLength > bytes.length) {\n        throw new RangeError(\n            `decodeBigEndianInt32sInto: out of bounds (offset=${offset}, byteLength=${byteLength}, bytes.length=${bytes.length})`,\n        );\n    }\n\n    const numCompleteInts = Math.floor(byteLength / 4);\n    const hasTrailingBytes = byteLength % 4 !== 0;\n    const numInts = hasTrailingBytes ? numCompleteInts + 1 : numCompleteInts;\n\n    if (out.length < numInts) {\n        throw new RangeError(`decodeBigEndianInt32sInto: out.length=${out.length} < ${numInts}`);\n    }\n\n    if (numCompleteInts > 0) {\n        const absoluteOffset = bytes.byteOffset + offset;\n        if ((absoluteOffset & 3) === 0) {\n            const u32 = new Uint32Array(bytes.buffer, absoluteOffset, numCompleteInts);\n            for (let i = 0; i < numCompleteInts; i++) {\n                out[i] = bswap32(u32[i]) | 0;\n            }\n        } else {\n            for (let i = 0; i < numCompleteInts; i++) {\n                const base = offset + i * 4;\n                out[i] = (bytes[base] << 24) | (bytes[base + 1] << 16) | (bytes[base + 2] << 8) | bytes[base + 3] | 0;\n            }\n        }\n    }\n\n    if (hasTrailingBytes) {\n        const base = offset + numCompleteInts * 4;\n        const remaining = byteLength - numCompleteInts * 4;\n        let v = 0;\n        for (let i = 0; i < remaining; i++) {\n            v |= bytes[base + i] << (24 - i * 8);\n        }\n        out[numCompleteInts] = v | 0;\n    }\n\n    return numInts;\n}\n"
  },
  {
    "path": "ts/src/decoding/decodingTestUtils.ts",
    "content": "import { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport { DictionaryType } from \"../metadata/tile/dictionaryType\";\nimport { LengthType } from \"../metadata/tile/lengthType\";\nimport { OffsetType } from \"../metadata/tile/offsetType\";\nimport IntWrapper from \"./intWrapper\";\nimport { type Column, type Field, ComplexType, ScalarType } from \"../metadata/tileset/tilesetMetadata\";\nimport { encodeBooleanRle, encodeStrings, createStringLengths } from \"../encoding/encodingUtils\";\nimport { encodeVarintInt32Value, encodeVarintInt32 } from \"../encoding/integerEncodingUtils\";\nimport type { RleEncodedStreamMetadata, StreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport type { LogicalStreamType } from \"../metadata/tile/logicalStreamType\";\n\n/**\n * Creates basic stream metadata with logical techniques.\n */\nexport function createStreamMetadata(\n    logicalTechnique1: LogicalLevelTechnique,\n    logicalTechnique2: LogicalLevelTechnique = LogicalLevelTechnique.NONE,\n    numValues = 3,\n): StreamMetadata {\n    return {\n        physicalStreamType: PhysicalStreamType.DATA,\n        logicalStreamType: { dictionaryType: DictionaryType.NONE },\n        logicalLevelTechnique1: logicalTechnique1,\n        logicalLevelTechnique2: logicalTechnique2,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues,\n        byteLength: 10,\n        decompressedCount: numValues,\n    };\n}\n\n/**\n * Creates RLE-encoded stream metadata.\n */\nexport function createRleMetadata(\n    logicalTechnique1: LogicalLevelTechnique,\n    logicalTechnique2: LogicalLevelTechnique,\n    runs: number,\n    numRleValues: number,\n): RleEncodedStreamMetadata {\n    return {\n        physicalStreamType: PhysicalStreamType.DATA,\n        logicalStreamType: { dictionaryType: DictionaryType.NONE },\n        logicalLevelTechnique1: logicalTechnique1,\n        logicalLevelTechnique2: logicalTechnique2,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues: runs * 2,\n        byteLength: 10,\n        decompressedCount: numRleValues,\n        runs,\n        numRleValues,\n    };\n}\n\n/**\n * Creates column metadata for STRUCT type columns.\n */\nexport function createColumnMetadataForStruct(\n    columnName: string,\n    childFields: Array<{ name: string; type?: number; nullable?: boolean }>,\n): Column {\n    const children: Field[] = childFields.map((fieldConfig) => ({\n        name: fieldConfig.name,\n        nullable: fieldConfig.nullable ?? true,\n        scalarField: {\n            physicalType: fieldConfig.type ?? ScalarType.STRING,\n            type: \"physicalType\" as const,\n        },\n        type: \"scalarField\" as const,\n    }));\n\n    return {\n        name: columnName,\n        nullable: false,\n        complexType: {\n            physicalType: ComplexType.STRUCT,\n            children,\n            type: \"physicalType\" as const,\n        },\n        type: \"complexType\" as const,\n    };\n}\n\n/**\n * Creates a single stream with metadata and data.\n */\nexport function createStream(\n    physicalType: PhysicalStreamType,\n    data: Uint8Array,\n    options: {\n        logical?: LogicalStreamType;\n        technique?: PhysicalLevelTechnique;\n        count?: number;\n    } = {},\n): Uint8Array {\n    const count = options.count ?? 0;\n    return buildEncodedStream(\n        {\n            physicalStreamType: physicalType,\n            logicalStreamType: options.logical ?? {},\n            logicalLevelTechnique1: LogicalLevelTechnique.NONE,\n            logicalLevelTechnique2: LogicalLevelTechnique.NONE,\n            physicalLevelTechnique: options.technique ?? PhysicalLevelTechnique.NONE,\n            numValues: count,\n            byteLength: data.length,\n            decompressedCount: count,\n        },\n        data,\n    );\n}\n\n/**\n * Encodes FSST-compressed strings into a complete stream.\n * This uses hardcoded test data: [\"cat\", \"dog\", \"cat\"]\n * @returns Encoded Uint8Array that can be passed to decodeString\n */\nexport function encodeFsstStrings(): Uint8Array {\n    const symbolTable = new Uint8Array([99, 97, 116, 100, 111, 103]); // \"catdog\"\n    const symbolLengths = new Uint32Array([3, 3]);\n    const compressedDictionary = new Uint8Array([0, 1]);\n    const dictionaryLengths = new Uint32Array([3, 3]);\n    const offsets = new Uint32Array([0, 1, 0]); // \"cat\", \"dog\", \"cat\"\n    const numValues = 3;\n\n    return concatenateBuffers(\n        createStream(PhysicalStreamType.PRESENT, encodeBooleanRle(new Array(numValues).fill(true)), {\n            technique: PhysicalLevelTechnique.VARINT,\n            count: numValues,\n        }),\n        createStream(PhysicalStreamType.DATA, symbolTable, {\n            logical: { dictionaryType: DictionaryType.FSST },\n        }),\n        createStream(PhysicalStreamType.LENGTH, encodeVarintInt32(symbolLengths), {\n            logical: { lengthType: LengthType.SYMBOL },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: symbolLengths.length,\n        }),\n        createStream(PhysicalStreamType.OFFSET, encodeVarintInt32(offsets), {\n            logical: { offsetType: OffsetType.STRING },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: offsets.length,\n        }),\n        createStream(PhysicalStreamType.LENGTH, encodeVarintInt32(dictionaryLengths), {\n            logical: { lengthType: LengthType.DICTIONARY },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: dictionaryLengths.length,\n        }),\n        createStream(PhysicalStreamType.DATA, compressedDictionary, {\n            logical: { dictionaryType: DictionaryType.SINGLE },\n        }),\n    );\n}\n\n/**\n * Encodes a shared dictionary for struct fields.\n * @param dictionaryStrings - Array of unique strings in the dictionary\n * @param options - Encoding options\n * @returns Object containing length and data streams\n */\nexport function encodeSharedDictionary(\n    dictionaryStrings: string[],\n    options: { useFsst?: boolean; dictionaryType?: DictionaryType } = {},\n): {\n    lengthStream: Uint8Array;\n    dataStream: Uint8Array;\n    symbolLengthStream?: Uint8Array;\n    symbolDataStream?: Uint8Array;\n} {\n    const { useFsst = false, dictionaryType = DictionaryType.SHARED } = options;\n\n    const encodedDictionary = encodeStrings(dictionaryStrings);\n    const dictionaryLengths = createStringLengths(dictionaryStrings);\n\n    const lengthStream = createStream(\n        PhysicalStreamType.LENGTH,\n        encodeVarintInt32(new Uint32Array(dictionaryLengths)),\n        {\n            logical: { lengthType: LengthType.DICTIONARY },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: dictionaryLengths.length,\n        },\n    );\n\n    const dataStream = createStream(PhysicalStreamType.DATA, encodedDictionary, {\n        logical: { dictionaryType: dictionaryType },\n        count: encodedDictionary.length,\n    });\n\n    if (useFsst) {\n        const symbolTable = new Uint8Array([99, 97, 116, 100, 111, 103]); // \"catdog\"\n        const symbolLengths = new Uint32Array([3, 3]);\n\n        const symbolLengthStream = createStream(PhysicalStreamType.LENGTH, encodeVarintInt32(symbolLengths), {\n            logical: { lengthType: LengthType.SYMBOL },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: symbolLengths.length,\n        });\n\n        const symbolDataStream = createStream(PhysicalStreamType.DATA, symbolTable, {\n            logical: { dictionaryType: DictionaryType.FSST },\n            count: symbolTable.length,\n        });\n\n        return { lengthStream, dataStream, symbolLengthStream, symbolDataStream };\n    }\n\n    return { lengthStream, dataStream };\n}\n\n/**\n * Encodes streams for a struct field.\n * @param offsetIndices - Indices into the shared dictionary\n * @param presentValues - Boolean array indicating which values are present\n * @param isPresent - Whether the field itself is present\n * @returns Encoded streams for the field\n */\nexport function encodeStructField(offsetIndices: number[], presentValues: boolean[], isPresent = true): Uint8Array {\n    if (!isPresent) {\n        return encodeNumStreams(0);\n    }\n\n    const numStreamsEncoded = encodeNumStreams(2);\n    const encodedPresent = createPresentStream(presentValues);\n    const encodedOffsets = createOffsetStream(offsetIndices);\n\n    return concatenateBuffers(numStreamsEncoded, encodedPresent, encodedOffsets);\n}\n\nfunction encodeNumStreams(numStreams: number): Uint8Array {\n    const buffer = new Uint8Array(5);\n    const offset = new IntWrapper(0);\n    encodeVarintInt32Value(numStreams, buffer, offset);\n    return buffer.slice(0, offset.get());\n}\n\nfunction createPresentStream(presentValues: boolean[]): Uint8Array {\n    const metadata = {\n        physicalStreamType: PhysicalStreamType.PRESENT,\n        logicalStreamType: { dictionaryType: DictionaryType.NONE },\n        logicalLevelTechnique1: LogicalLevelTechnique.NONE,\n        logicalLevelTechnique2: LogicalLevelTechnique.NONE,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues: presentValues.length,\n        byteLength: 0,\n        decompressedCount: presentValues.length,\n    };\n    return buildEncodedStream(metadata, encodeBooleanRle(presentValues));\n}\n\nfunction createOffsetStream(offsetIndices: number[]): Uint8Array {\n    const metadata = {\n        physicalStreamType: PhysicalStreamType.OFFSET,\n        logicalStreamType: { offsetType: OffsetType.STRING },\n        logicalLevelTechnique1: LogicalLevelTechnique.NONE,\n        logicalLevelTechnique2: LogicalLevelTechnique.NONE,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues: offsetIndices.length,\n        byteLength: 0,\n        decompressedCount: offsetIndices.length,\n    };\n    return buildEncodedStream(metadata, encodeVarintInt32(new Uint32Array(offsetIndices)));\n}\n\n/**\n * Builds a complete encoded stream by combining metadata and data.\n */\nexport function buildEncodedStream(\n    streamMetadata: StreamMetadata | RleEncodedStreamMetadata,\n    encodedData: Uint8Array,\n): Uint8Array {\n    const updatedMetadata = {\n        ...streamMetadata,\n        byteLength: encodedData.length,\n    };\n\n    const metadataBuffer = encodeStreamMetadata(updatedMetadata);\n    const result = new Uint8Array(metadataBuffer.length + encodedData.length);\n    result.set(metadataBuffer, 0);\n    result.set(encodedData, metadataBuffer.length);\n\n    return result;\n}\n\n/**\n * Encodes stream metadata into binary format.\n * - Byte 1: Stream type (physical type in upper 4 bits, logical subtype in lower 4 bits)\n * - Byte 2: Encodings (llt1[5-7], llt2[2-4], plt[0-1])\n * - Varints: numValues, byteLength\n * - If RLE: Varints: runs, numRleValues\n */\nexport function encodeStreamMetadata(metadata: StreamMetadata | RleEncodedStreamMetadata): Uint8Array {\n    const buffer = new Uint8Array(100);\n    let writeOffset = 0;\n\n    // Byte 1: Stream type\n    buffer[writeOffset++] = encodeStreamTypeByte(metadata);\n\n    // Byte 2: Encoding techniques\n    buffer[writeOffset++] = encodeEncodingsByte(metadata);\n\n    // Variable-length fields\n    const offset = new IntWrapper(writeOffset);\n    encodeVarintInt32Value(metadata.numValues, buffer, offset);\n    encodeVarintInt32Value(metadata.byteLength, buffer, offset);\n\n    // RLE-specific fields\n    if (isRleMetadata(metadata)) {\n        encodeVarintInt32Value(metadata.runs, buffer, offset);\n        encodeVarintInt32Value(metadata.numRleValues, buffer, offset);\n    }\n\n    return buffer.slice(0, offset.get());\n}\n\nfunction encodeStreamTypeByte(metadata: StreamMetadata | RleEncodedStreamMetadata): number {\n    const physicalTypeIndex = Object.values(PhysicalStreamType).indexOf(metadata.physicalStreamType);\n    const lowerNibble = getLogicalSubtypeValue(metadata);\n    return (physicalTypeIndex << 4) | lowerNibble;\n}\n\nfunction getLogicalSubtypeValue(metadata: StreamMetadata | RleEncodedStreamMetadata): number {\n    const { physicalStreamType, logicalStreamType } = metadata;\n\n    switch (physicalStreamType) {\n        case PhysicalStreamType.DATA:\n            return logicalStreamType.dictionaryType !== undefined\n                ? Object.values(DictionaryType).indexOf(logicalStreamType.dictionaryType)\n                : 0;\n        case PhysicalStreamType.OFFSET:\n            return logicalStreamType.offsetType !== undefined\n                ? Object.values(OffsetType).indexOf(logicalStreamType.offsetType)\n                : 0;\n        case PhysicalStreamType.LENGTH:\n            return logicalStreamType.lengthType !== undefined\n                ? Object.values(LengthType).indexOf(logicalStreamType.lengthType)\n                : 0;\n        default:\n            return 0;\n    }\n}\n\nfunction encodeEncodingsByte(metadata: StreamMetadata | RleEncodedStreamMetadata): number {\n    const llt1Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique1);\n    const llt2Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique2);\n    const pltIndex = Object.values(PhysicalLevelTechnique).indexOf(metadata.physicalLevelTechnique);\n    return (llt1Index << 5) | (llt2Index << 2) | pltIndex;\n}\n\nfunction isRleMetadata(metadata: StreamMetadata | RleEncodedStreamMetadata): metadata is RleEncodedStreamMetadata {\n    return \"runs\" in metadata && \"numRleValues\" in metadata;\n}\n\n/**\n * Concatenates multiple Uint8Array buffers into a single buffer.\n */\nexport function concatenateBuffers(...buffers: Uint8Array[]): Uint8Array {\n    const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0);\n    const result = new Uint8Array(totalLength);\n    let offset = 0;\n\n    for (const buffer of buffers) {\n        result.set(buffer, offset);\n        offset += buffer.length;\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "ts/src/decoding/decodingUtils.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport {\n    encodeBooleanRle,\n    encodeByteRle,\n    encodeDoubleLE,\n    encodeFloatsLE,\n    encodeStrings,\n} from \"../encoding/encodingUtils\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport { decodeBooleanRle, decodeByteRle, decodeDoublesLE, decodeFloatsLE, decodeString } from \"./decodingUtils\";\nimport IntWrapper from \"./intWrapper\";\n\ndescribe(\"decodingUtils\", () => {\n    describe(\"decodeFloatsLE\", () => {\n        it(\"should decode float values from little-endian bytes\", () => {\n            const data = new Float32Array([1.5, 2.5]);\n            const encoded = encodeFloatsLE(data);\n            const offset = new IntWrapper(0);\n            const result = decodeFloatsLE(encoded, offset, 2);\n\n            expect(result).toEqual(data);\n            expect(offset.get()).toBe(8);\n        });\n    });\n\n    describe(\"decodeDoublesLE\", () => {\n        it(\"should decode double values from little-endian bytes\", () => {\n            const data = new Float64Array([Math.PI, Math.E]);\n            const encoded = encodeDoubleLE(data);\n            const offset = new IntWrapper(0);\n            const result = decodeDoublesLE(encoded, offset, 2);\n\n            expect(result[0]).toBeCloseTo(Math.PI);\n            expect(result[1]).toBeCloseTo(Math.E);\n            expect(offset.get()).toBe(Float64Array.BYTES_PER_ELEMENT * 2);\n        });\n    });\n\n    describe(\"decodeFloatsLE with nullability\", () => {\n        it(\"should decode nullable float values with nullability buffer\", () => {\n            const data = new Float32Array([1.5, 2.5]);\n            const encoded = encodeFloatsLE(data);\n            const offset = new IntWrapper(0);\n            const bitVectorData = new Uint8Array([0b00000101]);\n            const nullabilityBuffer = new BitVector(bitVectorData, 3);\n\n            const result = decodeFloatsLE(encoded, offset, 2, nullabilityBuffer);\n\n            expect(result.length).toBe(3);\n            expect(result[0]).toBeCloseTo(1.5);\n            expect(result[1]).toBe(0);\n            expect(result[2]).toBeCloseTo(2.5);\n        });\n    });\n\n    describe(\"decodeDoublesLE with nullability\", () => {\n        it(\"should decode nullable double values with nullability buffer\", () => {\n            const data = new Float32Array([Math.PI, Math.E]);\n            const encoded = encodeDoubleLE(data);\n            const offset = new IntWrapper(0);\n            const bitVectorData = new Uint8Array([0b00000011]);\n            const nullabilityBuffer = new BitVector(bitVectorData, 2);\n\n            const result = decodeDoublesLE(encoded, offset, 2, nullabilityBuffer);\n\n            expect(result.length).toBe(2);\n            expect(result[0]).toBeCloseTo(Math.PI);\n            expect(result[1]).toBeCloseTo(Math.E);\n        });\n    });\n\n    describe(\"decodeBooleanRle\", () => {\n        it(\"should decode boolean RLE\", () => {\n            // Create 8 true boolean values\n            const data = [true, true, true, true, true, true, true, true];\n            const encoded = encodeBooleanRle(data);\n            const offset = new IntWrapper(0);\n            const result = decodeBooleanRle(encoded, 8, encoded.length, offset);\n\n            // All 8 bits should be set in the first byte\n            expect(result[0]).toBe(0xff);\n        });\n    });\n\n    describe(\"decodeByteRle\", () => {\n        it(\"should decode byte RLE with runs\", () => {\n            // Encode 5 identical bytes\n            const data = new Uint8Array([42, 42, 42, 42, 42]);\n            const encoded = encodeByteRle(data);\n            const offset = new IntWrapper(0);\n            const result = decodeByteRle(encoded, 5, encoded.length, offset);\n\n            expect(result).toEqual(data);\n        });\n\n        it(\"should decode byte RLE with literals\", () => {\n            // Encode 3 different bytes (will be encoded as literals)\n            const data = new Uint8Array([1, 2, 3]);\n            const encoded = encodeByteRle(data);\n            const offset = new IntWrapper(0);\n            const result = decodeByteRle(encoded, 3, encoded.length, offset);\n\n            expect(result).toEqual(data);\n            expect(offset.get()).toBe(encoded.length);\n        });\n\n        it(\"should handle truncated stream when byteLength runs out before numBytes\", () => {\n            // Request 10 bytes but byteLength only allows 2 bytes (header + value)\n            // header=0 means numRuns=3, but stream ends after value byte\n            const data = new Uint8Array([0, 42]);\n            const offset = new IntWrapper(0);\n            const result = decodeByteRle(data, 10, 2, offset);\n\n            // Should only fill 3 bytes (what the run specified) then stop at stream boundary\n            expect(result.length).toBe(10);\n            expect(result[0]).toBe(42);\n            expect(result[1]).toBe(42);\n            expect(result[2]).toBe(42);\n            // Remaining bytes should be 0\n            expect(result[3]).toBe(0);\n            expect(result[9]).toBe(0);\n            expect(offset.get()).toBe(2); // Should stop at byteLength boundary\n        });\n\n        it(\"should decode mixed literals and runs\", () => {\n            const data = new Uint8Array([1, 2, 5, 5, 5, 5, 5, 7, 8]);\n            const encoded = encodeByteRle(data);\n            const offset = new IntWrapper(0);\n            const result = decodeByteRle(encoded, 9, encoded.length, offset);\n\n            expect(result).toEqual(data);\n        });\n\n        it(\"should handle 128 literal max\", () => {\n            const data = new Uint8Array(130);\n            for (let i = 0; i < 130; i++) {\n                data[i] = i % 256;\n            }\n            const encoded = encodeByteRle(data);\n            const offset = new IntWrapper(0);\n            const result = decodeByteRle(encoded, 130, encoded.length, offset);\n\n            expect(result).toEqual(data);\n        });\n    });\n\n    describe(\"decodeString\", () => {\n        it(\"should decode short string\", () => {\n            const data = \"Hello\";\n            const encoded = encodeStrings([data]);\n            const result = decodeString(encoded, 0, encoded.length);\n\n            expect(result).toBe(data);\n        });\n\n        it(\"should decode long string\", () => {\n            const data = \"This is a longer string for testing TextDecoder path\";\n            const encoded = encodeStrings([data]);\n            const result = decodeString(encoded, 0, encoded.length);\n\n            expect(result).toBe(data);\n        });\n\n        it(\"should handle string with offset\", () => {\n            const prefix = \"Hello\";\n            const expectedText = \"World\";\n            const encoded = encodeStrings([prefix, expectedText]);\n            const prefixLength = new TextEncoder().encode(prefix).length;\n\n            const result = decodeString(encoded, prefixLength, encoded.length);\n\n            expect(result).toBe(expectedText);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/decodingUtils.ts",
    "content": "import type IntWrapper from \"./intWrapper\";\nimport { VectorType } from \"../vector/vectorType\";\nimport type BitVector from \"../vector/flat/bitVector\";\nimport { decodeStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport { unpackNullableBoolean, unpackNullable } from \"./unpackNullableUtils\";\n\nexport function skipColumn(numStreams: number, tile: Uint8Array, offset: IntWrapper) {\n    //TODO: add size of column in Mlt for fast skipping\n    for (let i = 0; i < numStreams; i++) {\n        const streamMetadata = decodeStreamMetadata(tile, offset);\n        offset.add(streamMetadata.byteLength);\n    }\n}\n\nexport function decodeBooleanRle(\n    buffer: Uint8Array,\n    numBooleans: number,\n    byteLength: number,\n    pos: IntWrapper,\n    nullabilityBuffer?: BitVector,\n): Uint8Array {\n    const numBytes = Math.ceil(numBooleans / 8.0);\n    const values = decodeByteRle(buffer, numBytes, byteLength, pos);\n\n    if (nullabilityBuffer) {\n        return unpackNullableBoolean(values, numBooleans, nullabilityBuffer);\n    }\n\n    return values;\n}\n\nexport function decodeByteRle(buffer: Uint8Array, numBytes: number, byteLength: number, pos: IntWrapper): Uint8Array {\n    const values = new Uint8Array(numBytes);\n\n    let valueOffset = 0;\n    const streamEndPos = pos.get() + byteLength;\n\n    while (valueOffset < numBytes) {\n        if (pos.get() >= streamEndPos) {\n            break;\n        }\n\n        const header = buffer[pos.increment()];\n        /* Runs */\n        if (header <= 0x7f) {\n            const numRuns = header + 3;\n            const value = buffer[pos.increment()];\n            const endValueOffset = Math.min(valueOffset + numRuns, numBytes);\n            values.fill(value, valueOffset, endValueOffset);\n            valueOffset = endValueOffset;\n        } else {\n            /* Literals */\n            const numLiterals = 256 - header;\n            for (let i = 0; i < numLiterals && valueOffset < numBytes; i++) {\n                values[valueOffset++] = buffer[pos.increment()];\n            }\n        }\n    }\n    pos.set(streamEndPos);\n    return values;\n}\n\nexport function decodeFloatsLE(\n    encodedValues: Uint8Array,\n    pos: IntWrapper,\n    numValues: number,\n    nullabilityBuffer?: BitVector,\n): Float32Array {\n    const currentPos = pos.get();\n    const newOffset = currentPos + numValues * Float32Array.BYTES_PER_ELEMENT;\n    const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;\n    const fb = new Float32Array(newBuf);\n    pos.set(newOffset);\n\n    if (nullabilityBuffer) {\n        return unpackNullable(fb, nullabilityBuffer, 0);\n    }\n\n    return fb;\n}\n\nexport function decodeDoublesLE(\n    encodedValues: Uint8Array,\n    pos: IntWrapper,\n    numValues: number,\n    nullabilityBuffer?: BitVector,\n): Float64Array {\n    const currentPos = pos.get();\n    const newOffset = currentPos + numValues * Float64Array.BYTES_PER_ELEMENT;\n    const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;\n    const fb = new Float64Array(newBuf);\n    pos.set(newOffset);\n\n    if (nullabilityBuffer) {\n        return unpackNullable(fb, nullabilityBuffer, 0);\n    }\n\n    return fb;\n}\n\nconst TEXT_DECODER_MIN_LENGTH = 12;\nconst utf8TextDecoder = new TextDecoder();\n\n// Source: https://github.com/mapbox/pbf/issues/106\nexport function decodeString(buf: Uint8Array, pos: number, end: number): string {\n    if (end - pos >= TEXT_DECODER_MIN_LENGTH) {\n        // longer strings are fast with the built-in browser TextDecoder API\n        return utf8TextDecoder.decode(buf.subarray(pos, end));\n    }\n    // short strings are fast with custom implementation\n    return readUtf8(buf, pos, end);\n}\n\nfunction readUtf8(buf, pos, end): string {\n    let str = \"\";\n    let i = pos;\n\n    while (i < end) {\n        const b0 = buf[i];\n        let c = null; // codepoint\n        let bytesPerSequence = b0 > 0xef ? 4 : b0 > 0xdf ? 3 : b0 > 0xbf ? 2 : 1;\n\n        if (i + bytesPerSequence > end) break;\n\n        let b1;\n        let b2;\n        let b3;\n\n        if (bytesPerSequence === 1) {\n            if (b0 < 0x80) {\n                c = b0;\n            }\n        } else if (bytesPerSequence === 2) {\n            b1 = buf[i + 1];\n            if ((b1 & 0xc0) === 0x80) {\n                c = ((b0 & 0x1f) << 0x6) | (b1 & 0x3f);\n                if (c <= 0x7f) {\n                    c = null;\n                }\n            }\n        } else if (bytesPerSequence === 3) {\n            b1 = buf[i + 1];\n            b2 = buf[i + 2];\n            if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80) {\n                c = ((b0 & 0xf) << 0xc) | ((b1 & 0x3f) << 0x6) | (b2 & 0x3f);\n                if (c <= 0x7ff || (c >= 0xd800 && c <= 0xdfff)) {\n                    c = null;\n                }\n            }\n        } else if (bytesPerSequence === 4) {\n            b1 = buf[i + 1];\n            b2 = buf[i + 2];\n            b3 = buf[i + 3];\n            if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80 && (b3 & 0xc0) === 0x80) {\n                c = ((b0 & 0xf) << 0x12) | ((b1 & 0x3f) << 0xc) | ((b2 & 0x3f) << 0x6) | (b3 & 0x3f);\n                if (c <= 0xffff || c >= 0x110000) {\n                    c = null;\n                }\n            }\n        }\n\n        if (c === null) {\n            c = 0xfffd;\n            bytesPerSequence = 1;\n        } else if (c > 0xffff) {\n            c -= 0x10000;\n            str += String.fromCharCode(((c >>> 10) & 0x3ff) | 0xd800);\n            c = 0xdc00 | (c & 0x3ff);\n        }\n\n        str += String.fromCharCode(c);\n        i += bytesPerSequence;\n    }\n\n    return str;\n}\n\nexport function getVectorTypeBooleanStream(\n    numFeatures: number,\n    byteLength: number,\n    data: Uint8Array,\n    offset: IntWrapper,\n): VectorType {\n    const valuesPerRun = 0x83;\n    // TODO: use VectorType metadata field for to test which VectorType is used\n    return Math.ceil(numFeatures / valuesPerRun) * 2 === byteLength &&\n        /* Test the first value byte if all bits are set to true */\n        (data[offset.get() + 1] & 0xff) === (bitCount(numFeatures) << 2) - 1\n        ? VectorType.CONST\n        : VectorType.FLAT;\n}\n\nfunction bitCount(number): number {\n    //TODO: refactor to get rid of special case handling\n    return number === 0 ? 1 : Math.floor(Math.log2(number) + 1);\n}\n"
  },
  {
    "path": "ts/src/decoding/fastPforCrossLanguage.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { readdirSync, readFileSync } from \"node:fs\";\n\nimport IntWrapper from \"./intWrapper\";\nimport { decodeBigEndianInt32sInto } from \"./bigEndianDecode\";\nimport { encodeFastPfor } from \"../encoding/integerEncodingUtils\";\nimport { createFastPforWireDecodeWorkspace, decodeFastPfor, decodeFastPforWithWorkspace } from \"./integerDecodingUtils\";\n\ndescribe(\"decodeFastPfor (wire format fixtures)\", () => {\n    const FIXTURES_DIR_URL = new URL(\"../../../test/fixtures/fastpfor/\", import.meta.url);\n\n    function fixtureUrl(fileName: string): URL {\n        return new URL(fileName, FIXTURES_DIR_URL);\n    }\n\n    function loadFixtureNames(): string[] {\n        return readdirSync(FIXTURES_DIR_URL, { withFileTypes: true })\n            .filter((entry) => entry.isFile() && entry.name.endsWith(\"_encoded.bin\"))\n            .map((entry) => entry.name.slice(0, -\"_encoded.bin\".length))\n            .sort();\n    }\n\n    function readEncodedFixtureBytes(name: string): Uint8Array {\n        const buf = readFileSync(fixtureUrl(`${name}_encoded.bin`));\n        return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n    }\n\n    function readExpectedFixtureValues(name: string): Uint32Array {\n        const buf = readFileSync(fixtureUrl(`${name}_decoded.bin`));\n        const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n        const out = new Uint32Array(bytes.byteLength >>> 2);\n        decodeBigEndianInt32sInto(bytes, 0, bytes.byteLength, out);\n        return out;\n    }\n\n    const fixtureNames = loadFixtureNames();\n    it(\"has FastPFOR fixtures\", () => {\n        expect(fixtureNames.length).toBeGreaterThan(0);\n    });\n    for (const name of fixtureNames) {\n        describe(name, () => {\n            it(\"decodes (no workspace)\", () => {\n                const encoded = readEncodedFixtureBytes(name);\n                const expectedValues = readExpectedFixtureValues(name);\n\n                const offset = new IntWrapper(0);\n                const decoded = decodeFastPfor(encoded, expectedValues.length, encoded.length, offset);\n                expect(decoded).toEqual(expectedValues);\n                expect(offset.get()).toBe(encoded.length);\n            });\n\n            it(\"decodes (with workspace reuse)\", () => {\n                const encoded = readEncodedFixtureBytes(name);\n                const expectedValues = readExpectedFixtureValues(name);\n                const workspace = createFastPforWireDecodeWorkspace();\n\n                const offset1 = new IntWrapper(0);\n                const decoded1 = decodeFastPforWithWorkspace(\n                    encoded,\n                    expectedValues.length,\n                    encoded.length,\n                    offset1,\n                    workspace,\n                );\n                expect(decoded1).toEqual(expectedValues);\n                expect(offset1.get()).toBe(encoded.length);\n\n                const offset2 = new IntWrapper(0);\n                const decoded2 = decodeFastPforWithWorkspace(\n                    encoded,\n                    expectedValues.length,\n                    encoded.length,\n                    offset2,\n                    workspace,\n                );\n                expect(decoded2).toEqual(expectedValues);\n                expect(offset2.get()).toBe(encoded.length);\n            });\n\n            it(\"does not depend on input ArrayBuffer alignment (prefix bytes)\", () => {\n                const encoded = readEncodedFixtureBytes(name);\n                const expectedValues = readExpectedFixtureValues(name);\n\n                const prefix = new Uint8Array([0xaa, 0xbb, 0xcc]);\n                const suffix = new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);\n\n                const buffer = new Uint8Array(prefix.length + encoded.length + suffix.length);\n                buffer.set(prefix, 0);\n                buffer.set(encoded, prefix.length);\n                buffer.set(suffix, prefix.length + encoded.length);\n\n                const offset = new IntWrapper(prefix.length);\n                const decoded = decodeFastPfor(buffer, expectedValues.length, encoded.length, offset);\n                expect(decoded).toEqual(expectedValues);\n                expect(offset.get()).toBe(prefix.length + encoded.length);\n                expect(buffer.subarray(prefix.length + encoded.length)).toEqual(suffix);\n            });\n\n            it(\"round-trips C++ decoded values through TS encode + decode\", () => {\n                const values = readExpectedFixtureValues(name);\n                const encoded = encodeFastPfor(values);\n\n                const offset = new IntWrapper(0);\n                const decoded = decodeFastPfor(encoded, values.length, encoded.length, offset);\n                expect(decoded).toEqual(values);\n                expect(offset.get()).toBe(encoded.length);\n            });\n\n            it(\"matches C++ encoded fixture bytes\", () => {\n                const fixtureEncoded = readEncodedFixtureBytes(name);\n                const values = readExpectedFixtureValues(name);\n                const encoded = encodeFastPfor(values);\n\n                expect(encoded).toEqual(fixtureEncoded);\n            });\n        });\n    }\n});\n"
  },
  {
    "path": "ts/src/decoding/fastPforDecoder.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\n\nimport { createFastPforEncoderWorkspace, encodeFastPforInt32WithWorkspace } from \"../encoding/fastPforEncoder\";\nimport {\n    createDecoderWorkspace,\n    createFastPforWireDecodeWorkspace,\n    decodeFastPforInt32,\n    ensureFastPforWireEncodedWordsCapacity,\n} from \"./fastPforDecoder\";\nimport { BLOCK_SIZE } from \"./fastPforShared\";\n\nconst LARGE_OUTLIER_VALUE = 1_048_576;\nconst DEFAULT_OUTLIER_VALUE = 4;\nconst HEADER_BYTE_MASK = 0xff;\nconst EXCEPTION_COUNT_SHIFT = 8;\nconst MAX_BITS_SHIFT = 16;\n\nfunction createSingleBlockValuesWithExceptionOutliers(outlierValue: number): Uint32Array {\n    const values = new Uint32Array(BLOCK_SIZE);\n    for (let i = 0; i < values.length; i++) values[i] = i % 2;\n    values[10] = outlierValue;\n    values[100] = outlierValue;\n    return values;\n}\n\nfunction getSinglePageWordLayout(encodedWords: Uint32Array) {\n    const metadataOffsetWordIndex = 1;\n    const metadataOffsetInWords = encodedWords[metadataOffsetWordIndex] | 0;\n    const packedDataEndWordIndex = (metadataOffsetWordIndex + metadataOffsetInWords) | 0;\n    const metadataByteLength = encodedWords[packedDataEndWordIndex] >>> 0;\n    const metadataWordCount = (metadataByteLength + 3) >>> 2;\n    const byteContainerStartWordIndex = (packedDataEndWordIndex + 1) | 0;\n    const exceptionBitmapWordIndex = (byteContainerStartWordIndex + metadataWordCount) | 0;\n    return { packedDataEndWordIndex, byteContainerStartWordIndex, exceptionBitmapWordIndex };\n}\n\nfunction parseBlockHeaderWord(headerWord: number): { bitWidth: number; exceptionCount: number; maxBits: number } {\n    return {\n        bitWidth: headerWord & HEADER_BYTE_MASK,\n        exceptionCount: (headerWord >>> EXCEPTION_COUNT_SHIFT) & HEADER_BYTE_MASK,\n        maxBits: (headerWord >>> MAX_BITS_SHIFT) & HEADER_BYTE_MASK,\n    };\n}\n\ndescribe(\"FastPFOR decoder\", () => {\n    it(\"throws on invalid alignedLength (not multiple of 256)\", () => {\n        expect(() => decodeFastPforInt32(new Uint32Array([1]), 0)).toThrow(/invalid alignedLength/);\n    });\n\n    it(\"throws when alignedLength exceeds output length\", () => {\n        expect(() => decodeFastPforInt32(new Uint32Array([BLOCK_SIZE]), 10)).toThrow(/output buffer too small/);\n    });\n\n    it(\"round-trips empty input\", () => {\n        const values = new Uint32Array(0);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"decodes an empty encoded buffer\", () => {\n        const decoded = decodeFastPforInt32(new Uint32Array(0), 0);\n        expect(decoded).toEqual(new Uint32Array(0));\n    });\n\n    it(\"throws when wire decode workspace capacity is negative\", () => {\n        expect(() => createFastPforWireDecodeWorkspace(-1)).toThrow(/must be >= 0/);\n    });\n\n    it(\"grows wire decode workspace encodedWords buffer on demand\", () => {\n        const workspace = createFastPforWireDecodeWorkspace(1);\n        const initialCapacity = workspace.encodedWords.length;\n\n        const reused = ensureFastPforWireEncodedWordsCapacity(workspace, initialCapacity);\n        expect(reused).toBe(workspace.encodedWords);\n\n        const grown = ensureFastPforWireEncodedWordsCapacity(workspace, initialCapacity + 1);\n        expect(grown).toBe(workspace.encodedWords);\n        expect(grown.length).toBeGreaterThan(initialCapacity);\n    });\n\n    it(\"round-trips VByte-only payload (<256 values)\", () => {\n        const values = new Uint32Array(100);\n        for (let i = 0; i < values.length; i++) values[i] = i * 7;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"round-trips exactly one aligned block (256 values)\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 31;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"round-trips full-width signed values\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = -(i + 1);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"round-trips representative block bit-widths\", () => {\n        const bitWidthCases = [\n            { bitWidth: 0, samples: [0] },\n            { bitWidth: 1, samples: [0, 1, 0, 1] },\n            { bitWidth: 2, samples: [2, 3, 2, 3] },\n            { bitWidth: 3, samples: [4, 5, 6, 7] },\n            { bitWidth: 4, samples: [8, 9, 10, 11] },\n            { bitWidth: 5, samples: [16, 17, 18, 19] },\n            { bitWidth: 6, samples: [32, 33, 34, 35] },\n            { bitWidth: 7, samples: [64, 65, 66, 67] },\n            { bitWidth: 8, samples: [128, 129, 130, 131] },\n            { bitWidth: 9, samples: [256, 257, 258, 259] },\n            { bitWidth: 12, samples: [2_048, 2_049, 2_050, 2_051] },\n            { bitWidth: 13, samples: [4_096, 4_097, 4_098, 4_099] },\n            { bitWidth: 14, samples: [8_192, 8_193, 8_194, 8_195] },\n            { bitWidth: 15, samples: [16_384, 16_385, 16_386, 16_387] },\n            { bitWidth: 16, samples: [32_768, 32_769, 32_770, 32_771] },\n            { bitWidth: 31, samples: [1_073_741_824, 1_073_741_825, 1_073_741_826, 1_073_741_827] },\n        ] as const;\n\n        for (const { bitWidth, samples } of bitWidthCases) {\n            const values = new Uint32Array(BLOCK_SIZE);\n            for (let i = 0; i < values.length; i++) values[i] = samples[i % samples.length];\n\n            const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n            const decoded = decodeFastPforInt32(encoded, values.length);\n            expect(decoded, `round-trip mismatch for bitWidth=${bitWidth}`).toEqual(values);\n        }\n    });\n\n    it(\"round-trips aligned blocks plus VByte tail\", () => {\n        const values = new Uint32Array(BLOCK_SIZE * 2 + 3);\n        for (let i = 0; i < values.length; i++) values[i] = i * 31;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"round-trips values with outliers (exceptions path)\", () => {\n        const values = new Uint32Array(BLOCK_SIZE * 2);\n        for (let i = 0; i < values.length; i++) values[i] = i % 16;\n        values[10] = 2_147_483_647;\n        values[200] = 1_073_741_824;\n        values[BLOCK_SIZE + 20] = 2_147_483_647;\n        values[BLOCK_SIZE + 210] = 1_073_741_824;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n\n    it(\"round-trips exception streams across widths\", () => {\n        const exceptionBitWidthCases = [\n            { exceptionBitWidth: 2, outlierValue: 4 },\n            { exceptionBitWidth: 3, outlierValue: 8 },\n            { exceptionBitWidth: 4, outlierValue: 16 },\n            { exceptionBitWidth: 5, outlierValue: 32 },\n            { exceptionBitWidth: 6, outlierValue: 64 },\n            { exceptionBitWidth: 7, outlierValue: 128 },\n            { exceptionBitWidth: 8, outlierValue: 256 },\n            { exceptionBitWidth: 9, outlierValue: 512 },\n            { exceptionBitWidth: 10, outlierValue: 1_024 },\n            { exceptionBitWidth: 11, outlierValue: 2_048 },\n            { exceptionBitWidth: 12, outlierValue: 4_096 },\n            { exceptionBitWidth: 13, outlierValue: 8_192 },\n            { exceptionBitWidth: 16, outlierValue: 65_536 },\n            { exceptionBitWidth: 32, outlierValue: -1 },\n        ] as const;\n\n        for (const { exceptionBitWidth, outlierValue } of exceptionBitWidthCases) {\n            const values = new Uint32Array(BLOCK_SIZE);\n            if (exceptionBitWidth === 32) {\n                values[0] = outlierValue;\n            } else {\n                for (let i = 0; i < values.length; i++) values[i] = i % 2;\n                values[10] = outlierValue;\n                values[100] = outlierValue;\n            }\n            const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n            const decoded = decodeFastPforInt32(encoded, values.length);\n            expect(decoded, `round-trip mismatch for exceptionBitWidth=${exceptionBitWidth}`).toEqual(values);\n        }\n    });\n\n    it(\"round-trips exceptionBitWidth=1 fast-path\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i & 1;\n        values[42] = 2;\n        values[128] = 2;\n\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { byteContainerStartWordIndex } = getSinglePageWordLayout(encoded);\n        const headerWord = encoded[byteContainerStartWordIndex] >>> 0;\n        const blockHeader = parseBlockHeaderWord(headerWord);\n\n        expect(blockHeader.exceptionCount).toBeGreaterThan(0);\n        expect(blockHeader.maxBits).toBe(blockHeader.bitWidth + 1);\n\n        const decoded = decodeFastPforInt32(encoded, values.length);\n        expect(decoded).toEqual(values);\n    });\n});\n\ndescribe(\"FastPFOR decoder error cases\", () => {\n    function withForcedByteSizeAndNoExceptionStreams(encoded: Uint32Array, forcedByteSize: number): Uint32Array {\n        const corrupted = encoded.slice();\n        const { packedDataEndWordIndex, byteContainerStartWordIndex } = getSinglePageWordLayout(corrupted);\n        corrupted[packedDataEndWordIndex] = forcedByteSize;\n\n        const bitmapWordIndex = byteContainerStartWordIndex + ((forcedByteSize + 3) >>> 2);\n        corrupted[bitmapWordIndex] = 0;\n        return corrupted;\n    }\n\n    it(\"throws on truncated input (missing page data)\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 31;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const truncated = encoded.subarray(0, 5);\n        expect(() => decodeFastPforInt32(truncated, values.length)).toThrow(/invalid whereMeta/);\n    });\n\n    it(\"throws on invalid whereMeta in page header\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 3;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const corruptedEncoded = encoded.slice();\n        corruptedEncoded[1] = 0;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/invalid whereMeta/);\n    });\n\n    it(\"throws on invalid block bitWidth in byte container\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 7;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { byteContainerStartWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        const blockHeaderWord = corruptedEncoded[byteContainerStartWordIndex] >>> 0;\n        corruptedEncoded[byteContainerStartWordIndex] = (blockHeaderWord & 0xffffff00) | 33 | 0;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/invalid bitWidth/);\n    });\n\n    it(\"throws on packed region mismatch when block metadata is inconsistent\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 31;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { byteContainerStartWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        const blockHeaderWord = corruptedEncoded[byteContainerStartWordIndex] >>> 0;\n        corruptedEncoded[byteContainerStartWordIndex] = (blockHeaderWord & 0xffffff00) | 0;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/packed region mismatch/);\n    });\n\n    it(\"throws on invalid maxBits in exception metadata\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(LARGE_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { byteContainerStartWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        const blockHeaderWord = corruptedEncoded[byteContainerStartWordIndex] >>> 0;\n        const { bitWidth: blockBitWidth } = parseBlockHeaderWord(blockHeaderWord);\n\n        const invalidMaxBits = (blockBitWidth - 1) & HEADER_BYTE_MASK;\n        const clearMaxBitsMask = ~(HEADER_BYTE_MASK << MAX_BITS_SHIFT);\n        corruptedEncoded[byteContainerStartWordIndex] =\n            (blockHeaderWord & clearMaxBitsMask) | (invalidMaxBits << MAX_BITS_SHIFT) | 0;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/invalid maxBits/);\n    });\n\n    it(\"throws on invalid byteSize that moves bitmap out of bounds\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { packedDataEndWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        corruptedEncoded[packedDataEndWordIndex] = 0x7fffffff;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/invalid byteSize/);\n    });\n\n    it(\"throws on truncated exception stream header\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 11;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { exceptionBitmapWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        corruptedEncoded[exceptionBitmapWordIndex] = corruptedEncoded[exceptionBitmapWordIndex] | (1 << 1);\n        const truncatedEncoded = corruptedEncoded.subarray(0, exceptionBitmapWordIndex + 1);\n\n        expect(() => decodeFastPforInt32(truncatedEncoded, values.length)).toThrow(/truncated exception stream header/);\n    });\n\n    it(\"throws on truncated exception stream payload\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * 13;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { exceptionBitmapWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = new Uint32Array(encoded.length + 1);\n        corruptedEncoded.set(encoded);\n        corruptedEncoded[exceptionBitmapWordIndex] = corruptedEncoded[exceptionBitmapWordIndex] | (1 << 1);\n        corruptedEncoded[exceptionBitmapWordIndex + 1] = 1;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/truncated exception stream/);\n    });\n\n    it(\"throws when byteSize is too small for exception metadata\", () => {\n        const smallByteSizeCases = [\n            { forcedByteSize: 1, expectedError: /byteContainer underflow/ },\n            { forcedByteSize: 2, expectedError: /exception header underflow/ },\n            { forcedByteSize: 4, expectedError: /exception positions underflow/ },\n        ];\n\n        for (const { forcedByteSize, expectedError } of smallByteSizeCases) {\n            const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n            const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n            const corruptedEncoded = withForcedByteSizeAndNoExceptionStreams(encoded, forcedByteSize);\n\n            expect(\n                () => decodeFastPforInt32(corruptedEncoded, values.length),\n                `forcedByteSize=${forcedByteSize}`,\n            ).toThrow(expectedError);\n        }\n    });\n\n    it(\"throws when maxBits equals bitWidth but exceptions are present\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { byteContainerStartWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        const blockHeaderWord = corruptedEncoded[byteContainerStartWordIndex] >>> 0;\n        const { bitWidth: blockBitWidth } = parseBlockHeaderWord(blockHeaderWord);\n        const bytes = new Uint8Array(corruptedEncoded.buffer, corruptedEncoded.byteOffset, corruptedEncoded.byteLength);\n        bytes[byteContainerStartWordIndex * 4 + 2] = blockBitWidth & HEADER_BYTE_MASK;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/invalid exceptionBitWidth=0/);\n    });\n\n    it(\"throws when exception stream is missing for declared exceptionBitWidth\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { exceptionBitmapWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        corruptedEncoded[exceptionBitmapWordIndex] = 0;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/missing exception stream/);\n    });\n\n    it(\"throws when exception stream pointer overflows stream size\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const { exceptionBitmapWordIndex } = getSinglePageWordLayout(encoded);\n\n        const corruptedEncoded = encoded.slice();\n        corruptedEncoded[exceptionBitmapWordIndex + 1] = 1;\n\n        expect(() => decodeFastPforInt32(corruptedEncoded, values.length)).toThrow(/exception stream overflow/);\n    });\n\n    it(\"throws on unterminated VByte value\", () => {\n        const encoded = new Uint32Array([0, 0x7f7f7f7f, 0x0000007f]);\n        expect(() => decodeFastPforInt32(encoded, 1)).toThrow(/unterminated value/);\n    });\n\n    it(\"throws when numValues exceeds decoded count\", () => {\n        const values = new Uint32Array(100);\n        for (let i = 0; i < values.length; i++) values[i] = i;\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        expect(() => decodeFastPforInt32(encoded, 200)).toThrow(/truncated stream/);\n    });\n});\n\ndescribe(\"FastPFOR decoder workspace paths\", () => {\n    it(\"reallocates byteContainer when provided workspace is too small\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const workspace = createDecoderWorkspace();\n        workspace.byteContainer = new Uint8Array(1);\n        workspace.byteContainerI32 = undefined;\n\n        const decoded = decodeFastPforInt32(encoded, values.length, workspace);\n        expect(decoded).toEqual(values);\n        expect(workspace.byteContainer.length).toBeGreaterThan(1);\n    });\n\n    it(\"handles unaligned byteContainer workspace via byte fallback copy path\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const workspace = createDecoderWorkspace();\n        const unalignedByteContainer = new Uint8Array(new ArrayBuffer(2049), 1, 2048);\n        workspace.byteContainer = unalignedByteContainer;\n        workspace.byteContainerI32 = undefined;\n\n        const decoded = decodeFastPforInt32(encoded, values.length, workspace);\n        expect(decoded).toEqual(values);\n        expect(workspace.byteContainer.byteOffset & 3).toBe(1);\n    });\n\n    it(\"rebuilds aligned int view when workspace has stale int32 view\", () => {\n        const values = createSingleBlockValuesWithExceptionOutliers(DEFAULT_OUTLIER_VALUE);\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const workspace = createDecoderWorkspace();\n        workspace.byteContainer = new Uint8Array(4096);\n        workspace.byteContainerI32 = new Int32Array(4);\n\n        const decoded = decodeFastPforInt32(encoded, values.length, workspace);\n        expect(decoded).toEqual(values);\n        const byteContainerI32 = workspace.byteContainerI32;\n        expect(byteContainerI32).toBeDefined();\n        expect(byteContainerI32?.buffer).toBe(workspace.byteContainer.buffer);\n        expect(byteContainerI32?.byteOffset).toBe(workspace.byteContainer.byteOffset);\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/fastPforDecoder.ts",
    "content": "import {\n    MASKS,\n    DEFAULT_PAGE_SIZE,\n    BLOCK_SIZE,\n    greatestMultiple,\n    roundUpToMultipleOf32,\n    normalizePageSize,\n} from \"./fastPforShared\";\nimport {\n    fastUnpack32_2,\n    fastUnpack32_3,\n    fastUnpack32_4,\n    fastUnpack32_5,\n    fastUnpack32_6,\n    fastUnpack32_7,\n    fastUnpack32_8,\n    fastUnpack32_9,\n    fastUnpack32_10,\n    fastUnpack32_11,\n    fastUnpack32_12,\n    fastUnpack32_16,\n    fastUnpack256_1,\n    fastUnpack256_2,\n    fastUnpack256_3,\n    fastUnpack256_4,\n    fastUnpack256_5,\n    fastUnpack256_6,\n    fastUnpack256_7,\n    fastUnpack256_8,\n    fastUnpack256_16,\n    fastUnpack256_Generic,\n} from \"./fastPforUnpack\";\n\n/**\n * FastPFOR decoding implementation.\n *\n * @remarks\n * Terminology note: \"exceptions\" in FastPFOR refer to **outlier values** within a block that do not fit in the\n * chosen base bit-width for that block. These are stored in separate \"exception streams\" and later applied back\n * to the unpacked base values. This is unrelated to JavaScript/TypeScript runtime exceptions.\n */\n\n/**\n * Workspace for the FastPFOR decoder.\n */\nexport type FastPforDecoderWorkspace = {\n    dataToBePacked: Array<Uint32Array>;\n    dataPointers: Int32Array;\n    byteContainer: Uint8Array;\n    byteContainerI32?: Int32Array;\n    exceptionSizes: Int32Array;\n};\n\n/**\n * Workspace for decoding the FastPFOR *wire format* (big-endian int32 words).\n *\n * @remarks\n * This workspace owns:\n * - a scratch `encodedWords` buffer to materialize big-endian words\n * - the reusable `FastPforDecoderWorkspace` used by `decodeFastPforInt32`\n *\n * The caller is responsible for creating and reusing this object.\n */\nexport type FastPforWireDecodeWorkspace = {\n    encodedWords: Uint32Array;\n    decoderWorkspace: FastPforDecoderWorkspace;\n};\n\nconst MAX_BIT_WIDTH = 32;\nconst BIT_WIDTH_SLOTS = MAX_BIT_WIDTH + 1;\n\nconst PAGE_SIZE = normalizePageSize(DEFAULT_PAGE_SIZE);\nconst BYTE_CONTAINER_SIZE = ((3 * PAGE_SIZE) / BLOCK_SIZE + PAGE_SIZE) | 0;\n\n/**\n * Creates an isolated workspace for decoding.\n * Reusing a workspace across calls avoids repeated allocations.\n */\nexport function createDecoderWorkspace(): FastPforDecoderWorkspace {\n    const byteContainer = new Uint8Array(BYTE_CONTAINER_SIZE);\n    return {\n        dataToBePacked: new Array(BIT_WIDTH_SLOTS),\n        dataPointers: new Int32Array(BIT_WIDTH_SLOTS),\n        byteContainer,\n        byteContainerI32: new Int32Array(\n            byteContainer.buffer,\n            byteContainer.byteOffset,\n            byteContainer.byteLength >>> 2,\n        ),\n        exceptionSizes: new Int32Array(BIT_WIDTH_SLOTS),\n    };\n}\n\nexport function createFastPforWireDecodeWorkspace(\n    initialEncodedWordCapacity: number = 16,\n): FastPforWireDecodeWorkspace {\n    if (initialEncodedWordCapacity < 0) {\n        throw new RangeError(`initialEncodedWordCapacity must be >= 0, got ${initialEncodedWordCapacity}`);\n    }\n\n    const capacity = Math.max(16, initialEncodedWordCapacity | 0);\n    return {\n        encodedWords: new Uint32Array(capacity),\n        decoderWorkspace: createDecoderWorkspace(),\n    };\n}\n\nexport function ensureFastPforWireEncodedWordsCapacity(\n    workspace: FastPforWireDecodeWorkspace,\n    requiredWordCount: number,\n): Uint32Array {\n    if (requiredWordCount <= workspace.encodedWords.length) return workspace.encodedWords;\n\n    const next = new Uint32Array(Math.max(16, requiredWordCount * 2));\n    workspace.encodedWords = next;\n    return next;\n}\n\nfunction materializeByteContainer(\n    inValues: Uint32Array,\n    byteContainerStart: number,\n    byteSize: number,\n    workspace: FastPforDecoderWorkspace,\n): Uint8Array {\n    if (workspace.byteContainer.length < byteSize) {\n        workspace.byteContainer = new Uint8Array(byteSize * 2);\n        workspace.byteContainerI32 = undefined;\n    }\n    const byteContainer = workspace.byteContainer;\n    const numFullInts = byteSize >>> 2;\n\n    if ((byteContainer.byteOffset & 3) === 0) {\n        let intView = workspace.byteContainerI32;\n        if (\n            !intView ||\n            intView.buffer !== byteContainer.buffer ||\n            intView.byteOffset !== byteContainer.byteOffset ||\n            intView.length < numFullInts\n        ) {\n            intView = workspace.byteContainerI32 = new Int32Array(\n                byteContainer.buffer,\n                byteContainer.byteOffset,\n                byteContainer.byteLength >>> 2,\n            );\n        }\n\n        intView.set(inValues.subarray(byteContainerStart, byteContainerStart + numFullInts));\n    } else {\n        for (let i = 0; i < numFullInts; i = (i + 1) | 0) {\n            const val = inValues[(byteContainerStart + i) | 0] | 0;\n            const base = i << 2;\n            byteContainer[base] = val & 0xff;\n            byteContainer[(base + 1) | 0] = (val >>> 8) & 0xff;\n            byteContainer[(base + 2) | 0] = (val >>> 16) & 0xff;\n            byteContainer[(base + 3) | 0] = (val >>> 24) & 0xff;\n        }\n    }\n\n    const remainder = byteSize & 3;\n    if (remainder > 0) {\n        const lastIntIdx = (byteContainerStart + numFullInts) | 0;\n        const lastVal = inValues[lastIntIdx] | 0;\n        const base = numFullInts << 2;\n        for (let r = 0; r < remainder; r = (r + 1) | 0) {\n            byteContainer[(base + r) | 0] = (lastVal >>> (r << 3)) & 0xff;\n        }\n    }\n\n    return byteContainer;\n}\n\n/**\n * Unpacks the per-bitWidth \"exception streams\" described by the page's bitmap.\n *\n * @remarks\n * For each bit-width present in the bitmap, a stream header gives the count of outlier values for that\n * bit-width, followed by packed bits representing those values.\n *\n * @param inValues - Packed input (32-bit words).\n * @param inExcept - Offset (32-bit word index) where the exception bitmap starts.\n * @param workspace - Decoder workspace used to store the unpacked exception streams.\n * @returns The new input offset (32-bit word index) after consuming all exception streams.\n */\nfunction unpackExceptionStreams(inValues: Uint32Array, inExcept: number, workspace: FastPforDecoderWorkspace): number {\n    const bitmap = inValues[inExcept++] | 0;\n    const dataToBePacked = workspace.dataToBePacked;\n\n    for (let bitWidth = 2; bitWidth <= MAX_BIT_WIDTH; bitWidth = (bitWidth + 1) | 0) {\n        if (((bitmap >>> (bitWidth - 1)) & 1) === 0) continue;\n\n        if (inExcept >= inValues.length) {\n            throw new Error(\n                `FastPFOR decode: truncated exception stream header (bitWidth=${bitWidth}, streamWordIndex=${inExcept}, needWords=1, availableWords=${inValues.length - inExcept}, encodedWords=${inValues.length})`,\n            );\n        }\n        const size = inValues[inExcept++] >>> 0;\n        const roundedUp = roundUpToMultipleOf32(size);\n\n        const wordsNeeded = (size * bitWidth + 31) >>> 5;\n        if (inExcept + wordsNeeded > inValues.length) {\n            throw new Error(\n                `FastPFOR decode: truncated exception stream (bitWidth=${bitWidth}, size=${size}, streamWordIndex=${inExcept}, needWords=${wordsNeeded}, availableWords=${inValues.length - inExcept}, encodedWords=${inValues.length})`,\n            );\n        }\n\n        let exceptionStream = dataToBePacked[bitWidth];\n        if (!exceptionStream || exceptionStream.length < roundedUp) {\n            exceptionStream = dataToBePacked[bitWidth] = new Uint32Array(roundedUp);\n        }\n\n        let j = 0;\n        for (; j < size; j = (j + 32) | 0) {\n            fastUnpack32(inValues, inExcept, exceptionStream, j, bitWidth);\n            inExcept = (inExcept + bitWidth) | 0;\n        }\n\n        const overflow = (j - size) | 0;\n        inExcept = (inExcept - ((overflow * bitWidth) >>> 5)) | 0;\n\n        workspace.exceptionSizes[bitWidth] = size;\n    }\n\n    return inExcept;\n}\n\n/**\n * Unpacks one 256-value block from the packed bitstream using a specialized implementation for common widths.\n *\n * @param inValues - Packed input (32-bit words).\n * @param inPos - Input offset (32-bit word index) where the packed block starts.\n * @param out - Output buffer.\n * @param outPos - Output offset where the 256 values will be written.\n * @param bitWidth - Base bit-width used for this block.\n * @returns The new input offset (32-bit word index) right after the packed block data.\n */\nfunction unpackBlock256(\n    inValues: Uint32Array,\n    inPos: number,\n    out: Uint32Array,\n    outPos: number,\n    bitWidth: number,\n): number {\n    switch (bitWidth) {\n        case 1:\n            fastUnpack256_1(inValues, inPos, out, outPos);\n            break;\n        case 2:\n            fastUnpack256_2(inValues, inPos, out, outPos);\n            break;\n        case 3:\n            fastUnpack256_3(inValues, inPos, out, outPos);\n            break;\n        case 4:\n            fastUnpack256_4(inValues, inPos, out, outPos);\n            break;\n        case 5:\n            fastUnpack256_5(inValues, inPos, out, outPos);\n            break;\n        case 6:\n            fastUnpack256_6(inValues, inPos, out, outPos);\n            break;\n        case 7:\n            fastUnpack256_7(inValues, inPos, out, outPos);\n            break;\n        case 8:\n            fastUnpack256_8(inValues, inPos, out, outPos);\n            break;\n        case 16:\n            fastUnpack256_16(inValues, inPos, out, outPos);\n            break;\n        default:\n            fastUnpack256_Generic(inValues, inPos, out, outPos, bitWidth);\n            break;\n    }\n\n    return (inPos + (bitWidth << 3)) | 0;\n}\n\n/**\n * Reads and validates the 2-byte block header from the byteContainer.\n *\n * @remarks\n * The header is `[bitWidth, exceptionCount]`, both stored as single bytes.\n *\n * @param byteContainer - Byte metadata buffer for the page.\n * @param byteContainerLen - The valid byte length in `byteContainer` for this page.\n * @param bytePosIn - Current offset in `byteContainer`.\n * @param block - Block index within the page (for error messages).\n * @returns The parsed header and the updated `bytePosIn`.\n */\nfunction readBlockHeader(\n    byteContainer: Uint8Array,\n    byteContainerLen: number,\n    bytePosIn: number,\n    block: number,\n): { bitWidth: number; exceptionCount: number; bytePosIn: number } {\n    if (bytePosIn + 2 > byteContainerLen) {\n        throw new Error(\n            `FastPFOR decode: byteContainer underflow at block=${block} (need 2 bytes for [bitWidth, exceptionCount], bytePos=${bytePosIn}, byteSize=${byteContainerLen})`,\n        );\n    }\n\n    const bitWidth = byteContainer[bytePosIn++];\n    const exceptionCount = byteContainer[bytePosIn++];\n\n    if (bitWidth > MAX_BIT_WIDTH) {\n        throw new Error(\n            `FastPFOR decode: invalid bitWidth=${bitWidth} at block=${block} (expected 0..${MAX_BIT_WIDTH}). This likely indicates corrupted or truncated input.`,\n        );\n    }\n\n    return { bitWidth, exceptionCount, bytePosIn };\n}\n\n/**\n * Reads and validates the exception header for a block.\n *\n * @remarks\n * The header contains `maxBits` (1 byte), which defines the width of the outlier values as\n * `exceptionBitWidth = maxBits - bitWidth`.\n *\n * @param byteContainer - Byte metadata buffer for the page.\n * @param byteContainerLen - The valid byte length in `byteContainer` for this page.\n * @param bytePosIn - Current offset in `byteContainer`.\n * @param bitWidth - Base bit-width for the block.\n * @param exceptionCount - Number of exceptions/outliers in this block.\n * @param block - Block index within the page (for error messages).\n * @returns Parsed `maxBits`, `exceptionBitWidth`, and the updated `bytePosIn`.\n */\nfunction readBlockExceptionHeader(\n    byteContainer: Uint8Array,\n    byteContainerLen: number,\n    bytePosIn: number,\n    bitWidth: number,\n    exceptionCount: number,\n    block: number,\n): { maxBits: number; exceptionBitWidth: number; bytePosIn: number } {\n    if (bytePosIn + 1 > byteContainerLen) {\n        throw new Error(\n            `FastPFOR decode: exception header underflow at block=${block} (need 1 byte for maxBits, bytePos=${bytePosIn}, byteSize=${byteContainerLen})`,\n        );\n    }\n    const maxBits = byteContainer[bytePosIn++];\n\n    if (maxBits < bitWidth || maxBits > MAX_BIT_WIDTH) {\n        throw new Error(\n            `FastPFOR decode: invalid maxBits=${maxBits} at block=${block} (bitWidth=${bitWidth}, expected ${bitWidth}..${MAX_BIT_WIDTH})`,\n        );\n    }\n    const exceptionBitWidth = (maxBits - bitWidth) | 0;\n    if (exceptionBitWidth < 1 || exceptionBitWidth > MAX_BIT_WIDTH) {\n        throw new Error(\n            `FastPFOR decode: invalid exceptionBitWidth=${exceptionBitWidth} at block=${block} (bitWidth=${bitWidth}, maxBits=${maxBits})`,\n        );\n    }\n\n    if (bytePosIn + exceptionCount > byteContainerLen) {\n        throw new Error(\n            `FastPFOR decode: exception positions underflow at block=${block} (need=${exceptionCount}, have=${byteContainerLen - bytePosIn})`,\n        );\n    }\n\n    return { maxBits, exceptionBitWidth, bytePosIn };\n}\n\n/**\n * Applies (block-local) FastPFOR \"exceptions\" (outliers) to an already-unpacked base 256-value block.\n *\n * @param out - Output buffer containing the base unpacked values for the block.\n * @param blockOutPos - Offset in `out` where the 256-value block starts.\n * @param bitWidth - Base bit-width for the block.\n * @param exceptionCount - Number of exceptions/outliers in this block.\n * @param byteContainer - Byte metadata buffer for the page.\n * @param byteContainerLen - The valid byte length in `byteContainer` for this page.\n * @param bytePosIn - Current offset in `byteContainer` (right after `[bitWidth, exceptionCount]`).\n * @param workspace - Decoder workspace holding the unpacked exception streams.\n * @param block - Block index within the page (for error messages).\n * @returns The updated `bytePosIn` after consuming the exception metadata bytes.\n *\n * The exception metadata is stored in `byteContainer`:\n * - `maxBits` (1 byte): the maximum bit-width of any value in the block\n * - `exceptionCount` exception positions (1 byte each, 0..255)\n *\n * The exception values themselves are read from the pre-unpacked exception streams stored in `workspace`.\n * Returns the new position in the byteContainer after consuming the exception metadata bytes.\n */\nfunction applyBlockExceptions(\n    out: Uint32Array,\n    blockOutPos: number,\n    bitWidth: number,\n    exceptionCount: number,\n    byteContainer: Uint8Array,\n    byteContainerLen: number,\n    bytePosIn: number,\n    workspace: FastPforDecoderWorkspace,\n    block: number,\n): number {\n    const {\n        maxBits,\n        exceptionBitWidth,\n        bytePosIn: afterHeaderPos,\n    } = readBlockExceptionHeader(byteContainer, byteContainerLen, bytePosIn, bitWidth, exceptionCount, block);\n    bytePosIn = afterHeaderPos;\n\n    if (exceptionBitWidth === 1) {\n        const shift = 1 << bitWidth;\n        for (let k = 0; k < exceptionCount; k = (k + 1) | 0) {\n            const pos = byteContainer[bytePosIn++];\n            out[(pos + blockOutPos) | 0] |= shift;\n        }\n        return bytePosIn;\n    }\n\n    const exceptionValues = workspace.dataToBePacked[exceptionBitWidth];\n    if (!exceptionValues) {\n        throw new Error(\n            `FastPFOR decode: missing exception stream for exceptionBitWidth=${exceptionBitWidth} (bitWidth=${bitWidth}, maxBits=${maxBits}) at block ${block}`,\n        );\n    }\n\n    const exceptionPointers = workspace.dataPointers;\n    let exPtr = exceptionPointers[exceptionBitWidth] | 0;\n    const exSize = workspace.exceptionSizes[exceptionBitWidth] | 0;\n\n    if (exPtr + exceptionCount > exSize) {\n        throw new Error(\n            `FastPFOR decode: exception stream overflow for exceptionBitWidth=${exceptionBitWidth} (ptr=${exPtr}, need ${exceptionCount}, size=${exSize}) at block ${block}`,\n        );\n    }\n\n    for (let k = 0; k < exceptionCount; k = (k + 1) | 0) {\n        const pos = byteContainer[bytePosIn++];\n        const val = exceptionValues[exPtr++] | 0;\n        out[(pos + blockOutPos) | 0] |= val << bitWidth;\n    }\n    exceptionPointers[exceptionBitWidth] = exPtr;\n    return bytePosIn;\n}\n\nfunction decodePageBlocks(\n    inValues: Uint32Array,\n    pageStart: number,\n    inPos: number,\n    packedEnd: number,\n    out: Uint32Array,\n    outPos: number,\n    blocks: number,\n    byteContainer: Uint8Array,\n    byteContainerLen: number,\n    workspace: FastPforDecoderWorkspace,\n): void {\n    let tmpInPos = inPos | 0;\n    let bytePosIn = 0;\n\n    for (let run = 0; run < blocks; run = (run + 1) | 0) {\n        const header = readBlockHeader(byteContainer, byteContainerLen, bytePosIn, run);\n        bytePosIn = header.bytePosIn;\n        const bitWidth = header.bitWidth;\n        const exceptionCount = header.exceptionCount;\n\n        const blockOutPos = (outPos + run * BLOCK_SIZE) | 0;\n\n        switch (bitWidth) {\n            case 0:\n                out.fill(0, blockOutPos, blockOutPos + BLOCK_SIZE);\n                break;\n\n            case 32:\n                for (let i = 0; i < BLOCK_SIZE; i = (i + 1) | 0) {\n                    out[(blockOutPos + i) | 0] = inValues[(tmpInPos + i) | 0] | 0;\n                }\n                tmpInPos = (tmpInPos + BLOCK_SIZE) | 0;\n                break;\n\n            default:\n                tmpInPos = unpackBlock256(inValues, tmpInPos, out, blockOutPos, bitWidth);\n                break;\n        }\n\n        if (exceptionCount > 0) {\n            bytePosIn = applyBlockExceptions(\n                out,\n                blockOutPos,\n                bitWidth,\n                exceptionCount,\n                byteContainer,\n                byteContainerLen,\n                bytePosIn,\n                workspace,\n                run,\n            );\n        }\n    }\n\n    if (tmpInPos !== packedEnd) {\n        throw new Error(\n            `FastPFOR decode: packed region mismatch (pageStart=${pageStart}, packedStart=${inPos}, consumedPackedEnd=${tmpInPos}, expectedPackedEnd=${packedEnd}, packedWords=${packedEnd - inPos}, encoded.length=${inValues.length})`,\n        );\n    }\n\n    return;\n}\n\n/**\n * Decodes one FastPFOR page (aligned to 256-value blocks).\n */\nfunction decodePage(\n    inValues: Uint32Array,\n    out: Uint32Array,\n    inPos: number,\n    outPos: number,\n    thisSize: number,\n    workspace: FastPforDecoderWorkspace,\n): number {\n    const pageStart = inPos | 0;\n    const whereMeta = inValues[pageStart] | 0;\n\n    if (whereMeta <= 0 || pageStart + whereMeta > inValues.length - 1) {\n        throw new Error(\n            `FastPFOR decode: invalid whereMeta=${whereMeta} at pageStart=${pageStart} (expected > 0 and pageStart+whereMeta < encoded.length=${inValues.length})`,\n        );\n    }\n\n    const packedStart = (pageStart + 1) | 0;\n    const packedEnd = (pageStart + whereMeta) | 0;\n\n    const byteSize = inValues[packedEnd] >>> 0;\n    const metaInts = (byteSize + 3) >>> 2;\n    const byteContainerStart = packedEnd + 1;\n    const bitmapPos = byteContainerStart + metaInts;\n\n    if (bitmapPos >= inValues.length) {\n        throw new Error(\n            `FastPFOR decode: invalid byteSize=${byteSize} (metaInts=${metaInts}, pageStart=${pageStart}, packedEnd=${packedEnd}, byteContainerStart=${byteContainerStart}) causes bitmapPos=${bitmapPos} out of bounds (encoded.length=${inValues.length})`,\n        );\n    }\n\n    const byteContainer = materializeByteContainer(inValues, byteContainerStart, byteSize, workspace);\n    const byteContainerLen = byteSize;\n\n    const inExcept = unpackExceptionStreams(inValues, bitmapPos, workspace);\n\n    const exceptionPointers = workspace.dataPointers;\n    exceptionPointers.fill(0);\n    const startOutPos = outPos | 0;\n    const blocks = (thisSize / BLOCK_SIZE) | 0;\n    decodePageBlocks(\n        inValues,\n        pageStart,\n        packedStart,\n        packedEnd,\n        out,\n        startOutPos,\n        blocks,\n        byteContainer,\n        byteContainerLen,\n        workspace,\n    );\n\n    return inExcept;\n}\n\nfunction decodeAlignedPages(\n    inValues: Uint32Array,\n    out: Uint32Array,\n    inPos: number,\n    outPos: number,\n    outLength: number,\n    workspace: FastPforDecoderWorkspace,\n): number {\n    const alignedOutLength = greatestMultiple(outLength, BLOCK_SIZE);\n    const finalOut = outPos + alignedOutLength;\n    let tmpOutPos = outPos;\n    let tmpInPos = inPos;\n\n    while (tmpOutPos !== finalOut) {\n        const thisSize = Math.min(PAGE_SIZE, finalOut - tmpOutPos);\n        tmpInPos = decodePage(inValues, out, tmpInPos, tmpOutPos, thisSize, workspace);\n        tmpOutPos = (tmpOutPos + thisSize) | 0;\n    }\n\n    return tmpInPos;\n}\n\n/**\n * Decodes the VariableByte tail (MSB=1 terminator, opposite of Protobuf Varint).\n */\nfunction decodeVByte(\n    inValues: Uint32Array,\n    inPos: number,\n    inLength: number,\n    out: Uint32Array,\n    outPos: number,\n    expectedCount: number,\n): number {\n    if (expectedCount === 0) return inPos;\n\n    let bitOffset = 0;\n    let wordIndex = inPos;\n    const finalWordIndex = inPos + inLength;\n    const outPos0 = outPos;\n    let tmpOutPos = outPos;\n    const targetOut = outPos + expectedCount;\n\n    let accumulator = 0;\n    let accumulatorShift = 0;\n\n    while (wordIndex < finalWordIndex && tmpOutPos < targetOut) {\n        const word = inValues[wordIndex];\n        const byte = (word >>> bitOffset) & 0xff;\n        bitOffset += 8;\n        wordIndex += bitOffset >>> 5;\n        bitOffset &= 31;\n\n        accumulator |= (byte & 0x7f) << accumulatorShift;\n        if ((byte & 0x80) !== 0) {\n            out[tmpOutPos++] = accumulator | 0;\n            accumulator = 0;\n            accumulatorShift = 0;\n        } else {\n            accumulatorShift += 7;\n            if (accumulatorShift > 28) {\n                throw new Error(\n                    `FastPFOR VByte: unterminated value (expected MSB=1 terminator within 5 bytes; shift=${accumulatorShift}, partial=${accumulator}, decoded=${tmpOutPos - outPos0}/${expectedCount}, inPos=${wordIndex}, inEnd=${finalWordIndex})`,\n                );\n            }\n        }\n    }\n\n    if (tmpOutPos !== targetOut) {\n        throw new Error(\n            `FastPFOR VByte: truncated stream (decoded=${tmpOutPos - outPos0}, expected=${expectedCount}, consumedWords=${wordIndex - inPos}/${inLength}, vbyteStart=${inPos}, vbyteEnd=${finalWordIndex})`,\n        );\n    }\n\n    return wordIndex;\n}\n\n/**\n * Decodes a sequence of FastPFOR-encoded integers.\n *\n * @param encoded The input buffer containing FastPFOR encoded data.\n * @param numValues The number of integers expected to be decoded.\n * @param workspace Optional workspace for reuse across calls. If omitted, a new workspace is created per call.\n */\nexport function decodeFastPforInt32(\n    encoded: Uint32Array,\n    numValues: number,\n    workspace?: FastPforDecoderWorkspace,\n): Uint32Array {\n    let inPos = 0;\n    let outPos = 0;\n    const decoded = new Uint32Array(numValues);\n\n    const decoderWorkspace = workspace ?? createDecoderWorkspace();\n\n    if (encoded.length > 0) {\n        const alignedLength = encoded[inPos] | 0;\n        inPos = (inPos + 1) | 0;\n\n        if ((alignedLength & (BLOCK_SIZE - 1)) !== 0) {\n            throw new Error(\n                `FastPFOR decode: invalid alignedLength=${alignedLength} (expected multiple of ${BLOCK_SIZE})`,\n            );\n        }\n\n        if (outPos + alignedLength > decoded.length) {\n            throw new Error(\n                `FastPFOR decode: output buffer too small (outPos=${outPos}, alignedLength=${alignedLength}, out.length=${decoded.length})`,\n            );\n        }\n\n        inPos = decodeAlignedPages(encoded, decoded, inPos, outPos, alignedLength, decoderWorkspace);\n        outPos = (outPos + alignedLength) | 0;\n    }\n\n    const remainingLength = (encoded.length - inPos) | 0;\n    const expectedTail = (numValues - outPos) | 0;\n    decodeVByte(encoded, inPos, remainingLength, decoded, outPos, expectedTail);\n\n    return decoded;\n}\n\nfunction fastUnpack32(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number, bitWidth: number): void {\n    switch (bitWidth) {\n        case 2:\n            fastUnpack32_2(inValues, inPos, out, outPos);\n            return;\n        case 3:\n            fastUnpack32_3(inValues, inPos, out, outPos);\n            return;\n        case 4:\n            fastUnpack32_4(inValues, inPos, out, outPos);\n            return;\n        case 5:\n            fastUnpack32_5(inValues, inPos, out, outPos);\n            return;\n        case 6:\n            fastUnpack32_6(inValues, inPos, out, outPos);\n            return;\n        case 7:\n            fastUnpack32_7(inValues, inPos, out, outPos);\n            return;\n        case 8:\n            fastUnpack32_8(inValues, inPos, out, outPos);\n            return;\n        case 9:\n            fastUnpack32_9(inValues, inPos, out, outPos);\n            return;\n        case 10:\n            fastUnpack32_10(inValues, inPos, out, outPos);\n            return;\n        case 11:\n            fastUnpack32_11(inValues, inPos, out, outPos);\n            return;\n        case 12:\n            fastUnpack32_12(inValues, inPos, out, outPos);\n            return;\n        case 16:\n            fastUnpack32_16(inValues, inPos, out, outPos);\n            return;\n        case 32:\n            for (let i = 0; i < 32; i = (i + 1) | 0) {\n                out[(outPos + i) | 0] = inValues[(inPos + i) | 0] | 0;\n            }\n            return;\n        default:\n            break;\n    }\n\n    const valueMask = MASKS[bitWidth] >>> 0;\n    let inputWordIndex = inPos;\n    let bitOffset = 0;\n    let currentWord = inValues[inputWordIndex] >>> 0;\n\n    for (let i = 0; i < 32; i++) {\n        if (bitOffset + bitWidth <= 32) {\n            const value = (currentWord >>> bitOffset) & valueMask;\n            out[outPos + i] = value | 0;\n            bitOffset += bitWidth;\n\n            if (bitOffset === 32) {\n                bitOffset = 0;\n                inputWordIndex++;\n                if (i !== 31) currentWord = inValues[inputWordIndex] >>> 0;\n            }\n        } else {\n            const lowBits = 32 - bitOffset;\n            const low = currentWord >>> bitOffset;\n\n            inputWordIndex++;\n            currentWord = inValues[inputWordIndex] >>> 0;\n            const highMask = MASKS[bitWidth - lowBits] >>> 0;\n            const high = currentWord & highMask;\n\n            const value = (low | (high << lowBits)) & valueMask;\n            out[outPos + i] = value | 0;\n            bitOffset = bitWidth - lowBits;\n        }\n    }\n}\n"
  },
  {
    "path": "ts/src/decoding/fastPforShared.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport {\n    BLOCK_SIZE,\n    DEFAULT_PAGE_SIZE,\n    bswap32,\n    greatestMultiple,\n    normalizePageSize,\n    roundUpToMultipleOf32,\n} from \"./fastPforShared\";\n\ndescribe(\"FastPforShared\", () => {\n    describe(\"endian helpers\", () => {\n        it(\"bswap32 swaps bytes\", () => {\n            expect(bswap32(0x11223344)).toBe(0x44332211);\n            expect(bswap32(0x00000000)).toBe(0x00000000);\n            expect(bswap32(0xffffffff)).toBe(0xffffffff);\n            expect(bswap32(0x89abcdef)).toBe(0xefcdab89);\n        });\n    });\n\n    describe(\"normalizePageSize\", () => {\n        it(\"returns DEFAULT_PAGE_SIZE for invalid inputs\", () => {\n            expect(normalizePageSize(0)).toBe(DEFAULT_PAGE_SIZE);\n            expect(normalizePageSize(-1)).toBe(DEFAULT_PAGE_SIZE);\n            expect(normalizePageSize(Number.NaN)).toBe(DEFAULT_PAGE_SIZE);\n            expect(normalizePageSize(Number.POSITIVE_INFINITY)).toBe(DEFAULT_PAGE_SIZE);\n            expect(normalizePageSize(Number.NEGATIVE_INFINITY)).toBe(DEFAULT_PAGE_SIZE);\n        });\n\n        it(\"rounds down to nearest multiple of BLOCK_SIZE\", () => {\n            expect(normalizePageSize(BLOCK_SIZE * 2 + 10)).toBe(BLOCK_SIZE * 2);\n            expect(normalizePageSize(BLOCK_SIZE * 10 + BLOCK_SIZE - 1)).toBe(BLOCK_SIZE * 10);\n        });\n\n        it(\"clamps small values to BLOCK_SIZE (min size)\", () => {\n            expect(normalizePageSize(1)).toBe(BLOCK_SIZE);\n            expect(normalizePageSize(BLOCK_SIZE - 1)).toBe(BLOCK_SIZE);\n            expect(normalizePageSize(BLOCK_SIZE)).toBe(BLOCK_SIZE);\n        });\n\n        it(\"handles float inputs by flooring\", () => {\n            expect(normalizePageSize(BLOCK_SIZE * 2.5)).toBe(BLOCK_SIZE * 2);\n        });\n\n        it(\"returns input when already a valid multiple of BLOCK_SIZE\", () => {\n            expect(normalizePageSize(BLOCK_SIZE * 4)).toBe(BLOCK_SIZE * 4);\n        });\n\n        it(\"handles large values\", () => {\n            expect(normalizePageSize(BLOCK_SIZE * 1_000)).toBe(BLOCK_SIZE * 1_000);\n            expect(normalizePageSize(BLOCK_SIZE * 1_000 + 123)).toBe(BLOCK_SIZE * 1_000);\n        });\n\n        it(\"returns DEFAULT_PAGE_SIZE for non-number inputs\", () => {\n            expect(normalizePageSize(undefined as any)).toBe(DEFAULT_PAGE_SIZE);\n            expect(normalizePageSize(null as any)).toBe(DEFAULT_PAGE_SIZE);\n        });\n    });\n\n    describe(\"greatestMultiple\", () => {\n        it(\"rounds down to the nearest multiple\", () => {\n            expect(greatestMultiple(10, 3)).toBe(9);\n            expect(greatestMultiple(12, 3)).toBe(12);\n        });\n    });\n\n    describe(\"roundUpToMultipleOf32\", () => {\n        it(\"rounds up to a multiple of 32\", () => {\n            expect(roundUpToMultipleOf32(0)).toBe(0);\n            expect(roundUpToMultipleOf32(1)).toBe(32);\n            expect(roundUpToMultipleOf32(32)).toBe(32);\n            expect(roundUpToMultipleOf32(33)).toBe(64);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/fastPforShared.ts",
    "content": "/**\n * Bit masks for each bitwidth 0-32.\n * DO NOT MUTATE - this is a shared constant.\n */\nconst masks = new Uint32Array(33);\nmasks[0] = 0;\nfor (let bitWidth = 1; bitWidth <= 32; bitWidth++) {\n    masks[bitWidth] = bitWidth === 32 ? 0xffffffff : 0xffffffff >>> (32 - bitWidth);\n}\nexport const MASKS: Readonly<Uint32Array> = masks;\n\nexport const DEFAULT_PAGE_SIZE = 65536;\nexport const BLOCK_SIZE = 256;\n\nexport function greatestMultiple(value: number, factor: number): number {\n    return value - (value % factor);\n}\n\nexport function roundUpToMultipleOf32(value: number): number {\n    return greatestMultiple(value + 31, 32);\n}\n\nexport function normalizePageSize(pageSize: number): number {\n    if (!Number.isFinite(pageSize) || pageSize <= 0) return DEFAULT_PAGE_SIZE;\n\n    const aligned = greatestMultiple(Math.floor(pageSize), BLOCK_SIZE);\n    return aligned === 0 ? BLOCK_SIZE : aligned;\n}\n\nexport function bswap32(value: number): number {\n    const x = value >>> 0;\n    return (((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x >>> 8) & 0xff00) | ((x >>> 24) & 0xff)) >>> 0;\n}\n"
  },
  {
    "path": "ts/src/decoding/fastPforUnpack.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { fastPack32 } from \"../encoding/fastPforEncoder\";\nimport { MASKS } from \"./fastPforShared\";\nimport {\n    fastUnpack32_1,\n    fastUnpack32_2,\n    fastUnpack32_3,\n    fastUnpack32_4,\n    fastUnpack32_5,\n    fastUnpack32_6,\n    fastUnpack32_7,\n    fastUnpack32_8,\n    fastUnpack32_9,\n    fastUnpack32_10,\n    fastUnpack32_11,\n    fastUnpack32_12,\n    fastUnpack32_16,\n    fastUnpack256_1,\n    fastUnpack256_2,\n    fastUnpack256_3,\n    fastUnpack256_4,\n    fastUnpack256_5,\n    fastUnpack256_6,\n    fastUnpack256_7,\n    fastUnpack256_8,\n    fastUnpack256_16,\n    fastUnpack256_Generic,\n} from \"./fastPforUnpack\";\n\ndescribe(\"FastPFOR Unpack Library\", () => {\n    type Unpacker = (inValues: Int32Array, inPos: number, out: Int32Array, outPos: number) => void;\n\n    const UNPACK32_TEST_CASES: Array<{ bitWidth: number; unpacker: Unpacker }> = [\n        { bitWidth: 1, unpacker: fastUnpack32_1 },\n        { bitWidth: 2, unpacker: fastUnpack32_2 },\n        { bitWidth: 3, unpacker: fastUnpack32_3 },\n        { bitWidth: 4, unpacker: fastUnpack32_4 },\n        { bitWidth: 5, unpacker: fastUnpack32_5 },\n        { bitWidth: 6, unpacker: fastUnpack32_6 },\n        { bitWidth: 7, unpacker: fastUnpack32_7 },\n        { bitWidth: 8, unpacker: fastUnpack32_8 },\n        { bitWidth: 9, unpacker: fastUnpack32_9 },\n        { bitWidth: 10, unpacker: fastUnpack32_10 },\n        { bitWidth: 11, unpacker: fastUnpack32_11 },\n        { bitWidth: 12, unpacker: fastUnpack32_12 },\n        { bitWidth: 16, unpacker: fastUnpack32_16 },\n    ];\n\n    const UNPACK256_SPECIALIZED_TEST_CASES: Array<{ bitWidth: number; unpacker: Unpacker }> = [\n        { bitWidth: 1, unpacker: fastUnpack256_1 },\n        { bitWidth: 2, unpacker: fastUnpack256_2 },\n        { bitWidth: 3, unpacker: fastUnpack256_3 },\n        { bitWidth: 4, unpacker: fastUnpack256_4 },\n        { bitWidth: 5, unpacker: fastUnpack256_5 },\n        { bitWidth: 6, unpacker: fastUnpack256_6 },\n        { bitWidth: 7, unpacker: fastUnpack256_7 },\n        { bitWidth: 8, unpacker: fastUnpack256_8 },\n        { bitWidth: 16, unpacker: fastUnpack256_16 },\n    ];\n\n    const UNPACK256_GENERIC_BIT_WIDTHS: number[] = [9, 10, 11, 12, 13, 14, 15];\n\n    function pack256(values: Int32Array, bitWidth: number): Int32Array {\n        const out = new Int32Array(bitWidth * 8);\n        for (let chunk = 0; chunk < 8; chunk++) {\n            fastPack32(values, chunk * 32, out, chunk * bitWidth, bitWidth);\n        }\n        return out;\n    }\n\n    function makeRamp(length: number, valueMask: number): Int32Array {\n        const values = new Int32Array(length);\n        for (let i = 0; i < length; i++) values[i] = i & valueMask;\n        return values;\n    }\n\n    function makeMaxPattern(length: number, valueMask: number): Int32Array {\n        return new Int32Array(length).fill(valueMask);\n    }\n\n    for (const { bitWidth, unpacker } of UNPACK32_TEST_CASES) {\n        describe(`fastUnpack32_${bitWidth}`, () => {\n            it(\"round-trips ramp\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeRamp(32, valueMask);\n\n                const input = new Int32Array(bitWidth);\n                fastPack32(expected, 0, input, 0, bitWidth);\n\n                const out = new Int32Array(32);\n                unpacker(input, 0, out, 0);\n                expect(out).toEqual(expected);\n            });\n\n            it(\"round-trips max\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeMaxPattern(32, valueMask);\n\n                const input = new Int32Array(bitWidth);\n                fastPack32(expected, 0, input, 0, bitWidth);\n\n                const out = new Int32Array(32);\n                unpacker(input, 0, out, 0);\n                expect(out).toEqual(expected);\n            });\n        });\n    }\n\n    for (const { bitWidth, unpacker } of UNPACK256_SPECIALIZED_TEST_CASES) {\n        describe(`fastUnpack256_${bitWidth}`, () => {\n            it(\"round-trips ramp\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeRamp(256, valueMask);\n                const input = pack256(expected, bitWidth);\n\n                const out = new Int32Array(256);\n                unpacker(input, 0, out, 0);\n                expect(out).toEqual(expected);\n            });\n\n            it(\"round-trips max\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeMaxPattern(256, valueMask);\n                const input = pack256(expected, bitWidth);\n\n                const out = new Int32Array(256);\n                unpacker(input, 0, out, 0);\n                expect(out).toEqual(expected);\n            });\n\n            it(\"matches fastUnpack256_Generic\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeRamp(256, valueMask);\n                const input = pack256(expected, bitWidth);\n\n                const outSpecific = new Int32Array(256);\n                const outGeneric = new Int32Array(256);\n                unpacker(input, 0, outSpecific, 0);\n                fastUnpack256_Generic(input, 0, outGeneric, 0, bitWidth);\n                expect(outSpecific).toEqual(outGeneric);\n            });\n        });\n    }\n\n    for (const bitWidth of UNPACK256_GENERIC_BIT_WIDTHS) {\n        describe(`fastUnpack256_Generic bitWidth=${bitWidth}`, () => {\n            it(\"round-trips ramp\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeRamp(256, valueMask);\n                const input = pack256(expected, bitWidth);\n\n                const out = new Int32Array(256);\n                fastUnpack256_Generic(input, 0, out, 0, bitWidth);\n                expect(out).toEqual(expected);\n            });\n\n            it(\"round-trips max\", () => {\n                const valueMask = MASKS[bitWidth] | 0;\n                const expected = makeMaxPattern(256, valueMask);\n                const input = pack256(expected, bitWidth);\n\n                const out = new Int32Array(256);\n                fastUnpack256_Generic(input, 0, out, 0, bitWidth);\n                expect(out).toEqual(expected);\n            });\n        });\n    }\n});\n"
  },
  {
    "path": "ts/src/decoding/fastPforUnpack.ts",
    "content": "import { MASKS } from \"./fastPforShared\";\n\nexport function fastUnpack32_1(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    const in0 = inValues[inPos] >>> 0;\n    for (let i = 0; i < 32; i++) {\n        out[outPos + i] = (in0 >>> i) & 1;\n    }\n}\n\nexport function fastUnpack32_2(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x3;\n    out[op++] = (in0 >>> 2) & 0x3;\n    out[op++] = (in0 >>> 4) & 0x3;\n    out[op++] = (in0 >>> 6) & 0x3;\n    out[op++] = (in0 >>> 8) & 0x3;\n    out[op++] = (in0 >>> 10) & 0x3;\n    out[op++] = (in0 >>> 12) & 0x3;\n    out[op++] = (in0 >>> 14) & 0x3;\n    out[op++] = (in0 >>> 16) & 0x3;\n    out[op++] = (in0 >>> 18) & 0x3;\n    out[op++] = (in0 >>> 20) & 0x3;\n    out[op++] = (in0 >>> 22) & 0x3;\n    out[op++] = (in0 >>> 24) & 0x3;\n    out[op++] = (in0 >>> 26) & 0x3;\n    out[op++] = (in0 >>> 28) & 0x3;\n    out[op++] = (in0 >>> 30) & 0x3;\n    out[op++] = (in1 >>> 0) & 0x3;\n    out[op++] = (in1 >>> 2) & 0x3;\n    out[op++] = (in1 >>> 4) & 0x3;\n    out[op++] = (in1 >>> 6) & 0x3;\n    out[op++] = (in1 >>> 8) & 0x3;\n    out[op++] = (in1 >>> 10) & 0x3;\n    out[op++] = (in1 >>> 12) & 0x3;\n    out[op++] = (in1 >>> 14) & 0x3;\n    out[op++] = (in1 >>> 16) & 0x3;\n    out[op++] = (in1 >>> 18) & 0x3;\n    out[op++] = (in1 >>> 20) & 0x3;\n    out[op++] = (in1 >>> 22) & 0x3;\n    out[op++] = (in1 >>> 24) & 0x3;\n    out[op++] = (in1 >>> 26) & 0x3;\n    out[op++] = (in1 >>> 28) & 0x3;\n    out[op] = (in1 >>> 30) & 0x3;\n}\n\nexport function fastUnpack32_3(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x7;\n    out[op++] = (in0 >>> 3) & 0x7;\n    out[op++] = (in0 >>> 6) & 0x7;\n    out[op++] = (in0 >>> 9) & 0x7;\n    out[op++] = (in0 >>> 12) & 0x7;\n    out[op++] = (in0 >>> 15) & 0x7;\n    out[op++] = (in0 >>> 18) & 0x7;\n    out[op++] = (in0 >>> 21) & 0x7;\n    out[op++] = (in0 >>> 24) & 0x7;\n    out[op++] = (in0 >>> 27) & 0x7;\n    out[op++] = ((in0 >>> 30) | ((in1 & 0x1) << 2)) & 0x7;\n    out[op++] = (in1 >>> 1) & 0x7;\n    out[op++] = (in1 >>> 4) & 0x7;\n    out[op++] = (in1 >>> 7) & 0x7;\n    out[op++] = (in1 >>> 10) & 0x7;\n    out[op++] = (in1 >>> 13) & 0x7;\n    out[op++] = (in1 >>> 16) & 0x7;\n    out[op++] = (in1 >>> 19) & 0x7;\n    out[op++] = (in1 >>> 22) & 0x7;\n    out[op++] = (in1 >>> 25) & 0x7;\n    out[op++] = (in1 >>> 28) & 0x7;\n    out[op++] = ((in1 >>> 31) | ((in2 & 0x3) << 1)) & 0x7;\n    out[op++] = (in2 >>> 2) & 0x7;\n    out[op++] = (in2 >>> 5) & 0x7;\n    out[op++] = (in2 >>> 8) & 0x7;\n    out[op++] = (in2 >>> 11) & 0x7;\n    out[op++] = (in2 >>> 14) & 0x7;\n    out[op++] = (in2 >>> 17) & 0x7;\n    out[op++] = (in2 >>> 20) & 0x7;\n    out[op++] = (in2 >>> 23) & 0x7;\n    out[op++] = (in2 >>> 26) & 0x7;\n    out[op] = (in2 >>> 29) & 0x7;\n}\n\nexport function fastUnpack32_4(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    out[op++] = (in0 >>> 0) & 0xf;\n    out[op++] = (in0 >>> 4) & 0xf;\n    out[op++] = (in0 >>> 8) & 0xf;\n    out[op++] = (in0 >>> 12) & 0xf;\n    out[op++] = (in0 >>> 16) & 0xf;\n    out[op++] = (in0 >>> 20) & 0xf;\n    out[op++] = (in0 >>> 24) & 0xf;\n    out[op++] = (in0 >>> 28) & 0xf;\n    out[op++] = (in1 >>> 0) & 0xf;\n    out[op++] = (in1 >>> 4) & 0xf;\n    out[op++] = (in1 >>> 8) & 0xf;\n    out[op++] = (in1 >>> 12) & 0xf;\n    out[op++] = (in1 >>> 16) & 0xf;\n    out[op++] = (in1 >>> 20) & 0xf;\n    out[op++] = (in1 >>> 24) & 0xf;\n    out[op++] = (in1 >>> 28) & 0xf;\n    out[op++] = (in2 >>> 0) & 0xf;\n    out[op++] = (in2 >>> 4) & 0xf;\n    out[op++] = (in2 >>> 8) & 0xf;\n    out[op++] = (in2 >>> 12) & 0xf;\n    out[op++] = (in2 >>> 16) & 0xf;\n    out[op++] = (in2 >>> 20) & 0xf;\n    out[op++] = (in2 >>> 24) & 0xf;\n    out[op++] = (in2 >>> 28) & 0xf;\n    out[op++] = (in3 >>> 0) & 0xf;\n    out[op++] = (in3 >>> 4) & 0xf;\n    out[op++] = (in3 >>> 8) & 0xf;\n    out[op++] = (in3 >>> 12) & 0xf;\n    out[op++] = (in3 >>> 16) & 0xf;\n    out[op++] = (in3 >>> 20) & 0xf;\n    out[op++] = (in3 >>> 24) & 0xf;\n    out[op] = (in3 >>> 28) & 0xf;\n}\n\nexport function fastUnpack32_5(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x1f;\n    out[op++] = (in0 >>> 5) & 0x1f;\n    out[op++] = (in0 >>> 10) & 0x1f;\n    out[op++] = (in0 >>> 15) & 0x1f;\n    out[op++] = (in0 >>> 20) & 0x1f;\n    out[op++] = (in0 >>> 25) & 0x1f;\n    out[op++] = ((in0 >>> 30) | ((in1 & 0x7) << 2)) & 0x1f;\n    out[op++] = (in1 >>> 3) & 0x1f;\n    out[op++] = (in1 >>> 8) & 0x1f;\n    out[op++] = (in1 >>> 13) & 0x1f;\n    out[op++] = (in1 >>> 18) & 0x1f;\n    out[op++] = (in1 >>> 23) & 0x1f;\n    out[op++] = ((in1 >>> 28) | ((in2 & 0x1) << 4)) & 0x1f;\n    out[op++] = (in2 >>> 1) & 0x1f;\n    out[op++] = (in2 >>> 6) & 0x1f;\n    out[op++] = (in2 >>> 11) & 0x1f;\n    out[op++] = (in2 >>> 16) & 0x1f;\n    out[op++] = (in2 >>> 21) & 0x1f;\n    out[op++] = (in2 >>> 26) & 0x1f;\n    out[op++] = ((in2 >>> 31) | ((in3 & 0xf) << 1)) & 0x1f;\n    out[op++] = (in3 >>> 4) & 0x1f;\n    out[op++] = (in3 >>> 9) & 0x1f;\n    out[op++] = (in3 >>> 14) & 0x1f;\n    out[op++] = (in3 >>> 19) & 0x1f;\n    out[op++] = (in3 >>> 24) & 0x1f;\n    out[op++] = ((in3 >>> 29) | ((in4 & 0x3) << 3)) & 0x1f;\n    out[op++] = (in4 >>> 2) & 0x1f;\n    out[op++] = (in4 >>> 7) & 0x1f;\n    out[op++] = (in4 >>> 12) & 0x1f;\n    out[op++] = (in4 >>> 17) & 0x1f;\n    out[op++] = (in4 >>> 22) & 0x1f;\n    out[op] = (in4 >>> 27) & 0x1f;\n}\n\nexport function fastUnpack32_6(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x3f;\n    out[op++] = (in0 >>> 6) & 0x3f;\n    out[op++] = (in0 >>> 12) & 0x3f;\n    out[op++] = (in0 >>> 18) & 0x3f;\n    out[op++] = (in0 >>> 24) & 0x3f;\n    out[op++] = ((in0 >>> 30) | ((in1 & 0xf) << 2)) & 0x3f;\n    out[op++] = (in1 >>> 4) & 0x3f;\n    out[op++] = (in1 >>> 10) & 0x3f;\n    out[op++] = (in1 >>> 16) & 0x3f;\n    out[op++] = (in1 >>> 22) & 0x3f;\n    out[op++] = ((in1 >>> 28) | ((in2 & 0x3) << 4)) & 0x3f;\n    out[op++] = (in2 >>> 2) & 0x3f;\n    out[op++] = (in2 >>> 8) & 0x3f;\n    out[op++] = (in2 >>> 14) & 0x3f;\n    out[op++] = (in2 >>> 20) & 0x3f;\n    out[op++] = (in2 >>> 26) & 0x3f;\n    out[op++] = (in3 >>> 0) & 0x3f;\n    out[op++] = (in3 >>> 6) & 0x3f;\n    out[op++] = (in3 >>> 12) & 0x3f;\n    out[op++] = (in3 >>> 18) & 0x3f;\n    out[op++] = (in3 >>> 24) & 0x3f;\n    out[op++] = ((in3 >>> 30) | ((in4 & 0xf) << 2)) & 0x3f;\n    out[op++] = (in4 >>> 4) & 0x3f;\n    out[op++] = (in4 >>> 10) & 0x3f;\n    out[op++] = (in4 >>> 16) & 0x3f;\n    out[op++] = (in4 >>> 22) & 0x3f;\n    out[op++] = ((in4 >>> 28) | ((in5 & 0x3) << 4)) & 0x3f;\n    out[op++] = (in5 >>> 2) & 0x3f;\n    out[op++] = (in5 >>> 8) & 0x3f;\n    out[op++] = (in5 >>> 14) & 0x3f;\n    out[op++] = (in5 >>> 20) & 0x3f;\n    out[op] = (in5 >>> 26) & 0x3f;\n}\n\nexport function fastUnpack32_7(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x7f;\n    out[op++] = (in0 >>> 7) & 0x7f;\n    out[op++] = (in0 >>> 14) & 0x7f;\n    out[op++] = (in0 >>> 21) & 0x7f;\n    out[op++] = ((in0 >>> 28) | ((in1 & 0x7) << 4)) & 0x7f;\n    out[op++] = (in1 >>> 3) & 0x7f;\n    out[op++] = (in1 >>> 10) & 0x7f;\n    out[op++] = (in1 >>> 17) & 0x7f;\n    out[op++] = (in1 >>> 24) & 0x7f;\n    out[op++] = ((in1 >>> 31) | ((in2 & 0x3f) << 1)) & 0x7f;\n    out[op++] = (in2 >>> 6) & 0x7f;\n    out[op++] = (in2 >>> 13) & 0x7f;\n    out[op++] = (in2 >>> 20) & 0x7f;\n    out[op++] = ((in2 >>> 27) | ((in3 & 0x3) << 5)) & 0x7f;\n    out[op++] = (in3 >>> 2) & 0x7f;\n    out[op++] = (in3 >>> 9) & 0x7f;\n    out[op++] = (in3 >>> 16) & 0x7f;\n    out[op++] = (in3 >>> 23) & 0x7f;\n    out[op++] = ((in3 >>> 30) | ((in4 & 0x1f) << 2)) & 0x7f;\n    out[op++] = (in4 >>> 5) & 0x7f;\n    out[op++] = (in4 >>> 12) & 0x7f;\n    out[op++] = (in4 >>> 19) & 0x7f;\n    out[op++] = ((in4 >>> 26) | ((in5 & 0x1) << 6)) & 0x7f;\n    out[op++] = (in5 >>> 1) & 0x7f;\n    out[op++] = (in5 >>> 8) & 0x7f;\n    out[op++] = (in5 >>> 15) & 0x7f;\n    out[op++] = (in5 >>> 22) & 0x7f;\n    out[op++] = ((in5 >>> 29) | ((in6 & 0xf) << 3)) & 0x7f;\n    out[op++] = (in6 >>> 4) & 0x7f;\n    out[op++] = (in6 >>> 11) & 0x7f;\n    out[op++] = (in6 >>> 18) & 0x7f;\n    out[op] = (in6 >>> 25) & 0x7f;\n}\n\nexport function fastUnpack32_8(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    out[op++] = (in0 >>> 0) & 0xff;\n    out[op++] = (in0 >>> 8) & 0xff;\n    out[op++] = (in0 >>> 16) & 0xff;\n    out[op++] = (in0 >>> 24) & 0xff;\n    out[op++] = (in1 >>> 0) & 0xff;\n    out[op++] = (in1 >>> 8) & 0xff;\n    out[op++] = (in1 >>> 16) & 0xff;\n    out[op++] = (in1 >>> 24) & 0xff;\n    out[op++] = (in2 >>> 0) & 0xff;\n    out[op++] = (in2 >>> 8) & 0xff;\n    out[op++] = (in2 >>> 16) & 0xff;\n    out[op++] = (in2 >>> 24) & 0xff;\n    out[op++] = (in3 >>> 0) & 0xff;\n    out[op++] = (in3 >>> 8) & 0xff;\n    out[op++] = (in3 >>> 16) & 0xff;\n    out[op++] = (in3 >>> 24) & 0xff;\n    out[op++] = (in4 >>> 0) & 0xff;\n    out[op++] = (in4 >>> 8) & 0xff;\n    out[op++] = (in4 >>> 16) & 0xff;\n    out[op++] = (in4 >>> 24) & 0xff;\n    out[op++] = (in5 >>> 0) & 0xff;\n    out[op++] = (in5 >>> 8) & 0xff;\n    out[op++] = (in5 >>> 16) & 0xff;\n    out[op++] = (in5 >>> 24) & 0xff;\n    out[op++] = (in6 >>> 0) & 0xff;\n    out[op++] = (in6 >>> 8) & 0xff;\n    out[op++] = (in6 >>> 16) & 0xff;\n    out[op++] = (in6 >>> 24) & 0xff;\n    out[op++] = (in7 >>> 0) & 0xff;\n    out[op++] = (in7 >>> 8) & 0xff;\n    out[op++] = (in7 >>> 16) & 0xff;\n    out[op] = (in7 >>> 24) & 0xff;\n}\n\nexport function fastUnpack32_9(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    const in8 = inValues[inPos + 8] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x1ff;\n    out[op++] = (in0 >>> 9) & 0x1ff;\n    out[op++] = (in0 >>> 18) & 0x1ff;\n    out[op++] = ((in0 >>> 27) | ((in1 & 0xf) << 5)) & 0x1ff;\n    out[op++] = (in1 >>> 4) & 0x1ff;\n    out[op++] = (in1 >>> 13) & 0x1ff;\n    out[op++] = (in1 >>> 22) & 0x1ff;\n    out[op++] = ((in1 >>> 31) | ((in2 & 0xff) << 1)) & 0x1ff;\n    out[op++] = (in2 >>> 8) & 0x1ff;\n    out[op++] = (in2 >>> 17) & 0x1ff;\n    out[op++] = ((in2 >>> 26) | ((in3 & 0x7) << 6)) & 0x1ff;\n    out[op++] = (in3 >>> 3) & 0x1ff;\n    out[op++] = (in3 >>> 12) & 0x1ff;\n    out[op++] = (in3 >>> 21) & 0x1ff;\n    out[op++] = ((in3 >>> 30) | ((in4 & 0x7f) << 2)) & 0x1ff;\n    out[op++] = (in4 >>> 7) & 0x1ff;\n    out[op++] = (in4 >>> 16) & 0x1ff;\n    out[op++] = ((in4 >>> 25) | ((in5 & 0x3) << 7)) & 0x1ff;\n    out[op++] = (in5 >>> 2) & 0x1ff;\n    out[op++] = (in5 >>> 11) & 0x1ff;\n    out[op++] = (in5 >>> 20) & 0x1ff;\n    out[op++] = ((in5 >>> 29) | ((in6 & 0x3f) << 3)) & 0x1ff;\n    out[op++] = (in6 >>> 6) & 0x1ff;\n    out[op++] = (in6 >>> 15) & 0x1ff;\n    out[op++] = ((in6 >>> 24) | ((in7 & 0x1) << 8)) & 0x1ff;\n    out[op++] = (in7 >>> 1) & 0x1ff;\n    out[op++] = (in7 >>> 10) & 0x1ff;\n    out[op++] = (in7 >>> 19) & 0x1ff;\n    out[op++] = ((in7 >>> 28) | ((in8 & 0x1f) << 4)) & 0x1ff;\n    out[op++] = (in8 >>> 5) & 0x1ff;\n    out[op++] = (in8 >>> 14) & 0x1ff;\n    out[op] = (in8 >>> 23) & 0x1ff;\n}\n\nexport function fastUnpack32_10(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    const in8 = inValues[inPos + 8] >>> 0;\n    const in9 = inValues[inPos + 9] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x3ff;\n    out[op++] = (in0 >>> 10) & 0x3ff;\n    out[op++] = (in0 >>> 20) & 0x3ff;\n    out[op++] = ((in0 >>> 30) | ((in1 & 0xff) << 2)) & 0x3ff;\n    out[op++] = (in1 >>> 8) & 0x3ff;\n    out[op++] = (in1 >>> 18) & 0x3ff;\n    out[op++] = ((in1 >>> 28) | ((in2 & 0x3f) << 4)) & 0x3ff;\n    out[op++] = (in2 >>> 6) & 0x3ff;\n    out[op++] = (in2 >>> 16) & 0x3ff;\n    out[op++] = ((in2 >>> 26) | ((in3 & 0xf) << 6)) & 0x3ff;\n    out[op++] = (in3 >>> 4) & 0x3ff;\n    out[op++] = (in3 >>> 14) & 0x3ff;\n    out[op++] = ((in3 >>> 24) | ((in4 & 0x3) << 8)) & 0x3ff;\n    out[op++] = (in4 >>> 2) & 0x3ff;\n    out[op++] = (in4 >>> 12) & 0x3ff;\n    out[op++] = (in4 >>> 22) & 0x3ff;\n    out[op++] = (in5 >>> 0) & 0x3ff;\n    out[op++] = (in5 >>> 10) & 0x3ff;\n    out[op++] = (in5 >>> 20) & 0x3ff;\n    out[op++] = ((in5 >>> 30) | ((in6 & 0xff) << 2)) & 0x3ff;\n    out[op++] = (in6 >>> 8) & 0x3ff;\n    out[op++] = (in6 >>> 18) & 0x3ff;\n    out[op++] = ((in6 >>> 28) | ((in7 & 0x3f) << 4)) & 0x3ff;\n    out[op++] = (in7 >>> 6) & 0x3ff;\n    out[op++] = (in7 >>> 16) & 0x3ff;\n    out[op++] = ((in7 >>> 26) | ((in8 & 0xf) << 6)) & 0x3ff;\n    out[op++] = (in8 >>> 4) & 0x3ff;\n    out[op++] = (in8 >>> 14) & 0x3ff;\n    out[op++] = ((in8 >>> 24) | ((in9 & 0x3) << 8)) & 0x3ff;\n    out[op++] = (in9 >>> 2) & 0x3ff;\n    out[op++] = (in9 >>> 12) & 0x3ff;\n    out[op] = (in9 >>> 22) & 0x3ff;\n}\n\nexport function fastUnpack32_11(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    const in8 = inValues[inPos + 8] >>> 0;\n    const in9 = inValues[inPos + 9] >>> 0;\n    const in10 = inValues[inPos + 10] >>> 0;\n    out[op++] = (in0 >>> 0) & 0x7ff;\n    out[op++] = (in0 >>> 11) & 0x7ff;\n    out[op++] = ((in0 >>> 22) | ((in1 & 0x1) << 10)) & 0x7ff;\n    out[op++] = (in1 >>> 1) & 0x7ff;\n    out[op++] = (in1 >>> 12) & 0x7ff;\n    out[op++] = ((in1 >>> 23) | ((in2 & 0x3) << 9)) & 0x7ff;\n    out[op++] = (in2 >>> 2) & 0x7ff;\n    out[op++] = (in2 >>> 13) & 0x7ff;\n    out[op++] = ((in2 >>> 24) | ((in3 & 0x7) << 8)) & 0x7ff;\n    out[op++] = (in3 >>> 3) & 0x7ff;\n    out[op++] = (in3 >>> 14) & 0x7ff;\n    out[op++] = ((in3 >>> 25) | ((in4 & 0xf) << 7)) & 0x7ff;\n    out[op++] = (in4 >>> 4) & 0x7ff;\n    out[op++] = (in4 >>> 15) & 0x7ff;\n    out[op++] = ((in4 >>> 26) | ((in5 & 0x1f) << 6)) & 0x7ff;\n    out[op++] = (in5 >>> 5) & 0x7ff;\n    out[op++] = (in5 >>> 16) & 0x7ff;\n    out[op++] = ((in5 >>> 27) | ((in6 & 0x3f) << 5)) & 0x7ff;\n    out[op++] = (in6 >>> 6) & 0x7ff;\n    out[op++] = (in6 >>> 17) & 0x7ff;\n    out[op++] = ((in6 >>> 28) | ((in7 & 0x7f) << 4)) & 0x7ff;\n    out[op++] = (in7 >>> 7) & 0x7ff;\n    out[op++] = (in7 >>> 18) & 0x7ff;\n    out[op++] = ((in7 >>> 29) | ((in8 & 0xff) << 3)) & 0x7ff;\n    out[op++] = (in8 >>> 8) & 0x7ff;\n    out[op++] = (in8 >>> 19) & 0x7ff;\n    out[op++] = ((in8 >>> 30) | ((in9 & 0x1ff) << 2)) & 0x7ff;\n    out[op++] = (in9 >>> 9) & 0x7ff;\n    out[op++] = (in9 >>> 20) & 0x7ff;\n    out[op++] = ((in9 >>> 31) | ((in10 & 0x3ff) << 1)) & 0x7ff;\n    out[op++] = (in10 >>> 10) & 0x7ff;\n    out[op] = (in10 >>> 21) & 0x7ff;\n}\n\nexport function fastUnpack32_12(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    const in8 = inValues[inPos + 8] >>> 0;\n    const in9 = inValues[inPos + 9] >>> 0;\n    const in10 = inValues[inPos + 10] >>> 0;\n    const in11 = inValues[inPos + 11] >>> 0;\n    out[op++] = (in0 >>> 0) & 0xfff;\n    out[op++] = (in0 >>> 12) & 0xfff;\n    out[op++] = ((in0 >>> 24) | ((in1 & 0xf) << 8)) & 0xfff;\n    out[op++] = (in1 >>> 4) & 0xfff;\n    out[op++] = (in1 >>> 16) & 0xfff;\n    out[op++] = ((in1 >>> 28) | ((in2 & 0xff) << 4)) & 0xfff;\n    out[op++] = (in2 >>> 8) & 0xfff;\n    out[op++] = (in2 >>> 20) & 0xfff;\n    out[op++] = (in3 >>> 0) & 0xfff;\n    out[op++] = (in3 >>> 12) & 0xfff;\n    out[op++] = ((in3 >>> 24) | ((in4 & 0xf) << 8)) & 0xfff;\n    out[op++] = (in4 >>> 4) & 0xfff;\n    out[op++] = (in4 >>> 16) & 0xfff;\n    out[op++] = ((in4 >>> 28) | ((in5 & 0xff) << 4)) & 0xfff;\n    out[op++] = (in5 >>> 8) & 0xfff;\n    out[op++] = (in5 >>> 20) & 0xfff;\n    out[op++] = (in6 >>> 0) & 0xfff;\n    out[op++] = (in6 >>> 12) & 0xfff;\n    out[op++] = ((in6 >>> 24) | ((in7 & 0xf) << 8)) & 0xfff;\n    out[op++] = (in7 >>> 4) & 0xfff;\n    out[op++] = (in7 >>> 16) & 0xfff;\n    out[op++] = ((in7 >>> 28) | ((in8 & 0xff) << 4)) & 0xfff;\n    out[op++] = (in8 >>> 8) & 0xfff;\n    out[op++] = (in8 >>> 20) & 0xfff;\n    out[op++] = (in9 >>> 0) & 0xfff;\n    out[op++] = (in9 >>> 12) & 0xfff;\n    out[op++] = ((in9 >>> 24) | ((in10 & 0xf) << 8)) & 0xfff;\n    out[op++] = (in10 >>> 4) & 0xfff;\n    out[op++] = (in10 >>> 16) & 0xfff;\n    out[op++] = ((in10 >>> 28) | ((in11 & 0xff) << 4)) & 0xfff;\n    out[op++] = (in11 >>> 8) & 0xfff;\n    out[op] = (in11 >>> 20) & 0xfff;\n}\n\nexport function fastUnpack32_16(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    const in0 = inValues[inPos] >>> 0;\n    const in1 = inValues[inPos + 1] >>> 0;\n    const in2 = inValues[inPos + 2] >>> 0;\n    const in3 = inValues[inPos + 3] >>> 0;\n    const in4 = inValues[inPos + 4] >>> 0;\n    const in5 = inValues[inPos + 5] >>> 0;\n    const in6 = inValues[inPos + 6] >>> 0;\n    const in7 = inValues[inPos + 7] >>> 0;\n    const in8 = inValues[inPos + 8] >>> 0;\n    const in9 = inValues[inPos + 9] >>> 0;\n    const in10 = inValues[inPos + 10] >>> 0;\n    const in11 = inValues[inPos + 11] >>> 0;\n    const in12 = inValues[inPos + 12] >>> 0;\n    const in13 = inValues[inPos + 13] >>> 0;\n    const in14 = inValues[inPos + 14] >>> 0;\n    const in15 = inValues[inPos + 15] >>> 0;\n    out[op++] = (in0 >>> 0) & 0xffff;\n    out[op++] = (in0 >>> 16) & 0xffff;\n    out[op++] = (in1 >>> 0) & 0xffff;\n    out[op++] = (in1 >>> 16) & 0xffff;\n    out[op++] = (in2 >>> 0) & 0xffff;\n    out[op++] = (in2 >>> 16) & 0xffff;\n    out[op++] = (in3 >>> 0) & 0xffff;\n    out[op++] = (in3 >>> 16) & 0xffff;\n    out[op++] = (in4 >>> 0) & 0xffff;\n    out[op++] = (in4 >>> 16) & 0xffff;\n    out[op++] = (in5 >>> 0) & 0xffff;\n    out[op++] = (in5 >>> 16) & 0xffff;\n    out[op++] = (in6 >>> 0) & 0xffff;\n    out[op++] = (in6 >>> 16) & 0xffff;\n    out[op++] = (in7 >>> 0) & 0xffff;\n    out[op++] = (in7 >>> 16) & 0xffff;\n    out[op++] = (in8 >>> 0) & 0xffff;\n    out[op++] = (in8 >>> 16) & 0xffff;\n    out[op++] = (in9 >>> 0) & 0xffff;\n    out[op++] = (in9 >>> 16) & 0xffff;\n    out[op++] = (in10 >>> 0) & 0xffff;\n    out[op++] = (in10 >>> 16) & 0xffff;\n    out[op++] = (in11 >>> 0) & 0xffff;\n    out[op++] = (in11 >>> 16) & 0xffff;\n    out[op++] = (in12 >>> 0) & 0xffff;\n    out[op++] = (in12 >>> 16) & 0xffff;\n    out[op++] = (in13 >>> 0) & 0xffff;\n    out[op++] = (in13 >>> 16) & 0xffff;\n    out[op++] = (in14 >>> 0) & 0xffff;\n    out[op++] = (in14 >>> 16) & 0xffff;\n    out[op++] = (in15 >>> 0) & 0xffff;\n    out[op] = (in15 >>> 16) & 0xffff;\n}\n\nexport function fastUnpack256_1(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x1;\n        out[op++] = (in0 >>> 1) & 0x1;\n        out[op++] = (in0 >>> 2) & 0x1;\n        out[op++] = (in0 >>> 3) & 0x1;\n        out[op++] = (in0 >>> 4) & 0x1;\n        out[op++] = (in0 >>> 5) & 0x1;\n        out[op++] = (in0 >>> 6) & 0x1;\n        out[op++] = (in0 >>> 7) & 0x1;\n        out[op++] = (in0 >>> 8) & 0x1;\n        out[op++] = (in0 >>> 9) & 0x1;\n        out[op++] = (in0 >>> 10) & 0x1;\n        out[op++] = (in0 >>> 11) & 0x1;\n        out[op++] = (in0 >>> 12) & 0x1;\n        out[op++] = (in0 >>> 13) & 0x1;\n        out[op++] = (in0 >>> 14) & 0x1;\n        out[op++] = (in0 >>> 15) & 0x1;\n        out[op++] = (in0 >>> 16) & 0x1;\n        out[op++] = (in0 >>> 17) & 0x1;\n        out[op++] = (in0 >>> 18) & 0x1;\n        out[op++] = (in0 >>> 19) & 0x1;\n        out[op++] = (in0 >>> 20) & 0x1;\n        out[op++] = (in0 >>> 21) & 0x1;\n        out[op++] = (in0 >>> 22) & 0x1;\n        out[op++] = (in0 >>> 23) & 0x1;\n        out[op++] = (in0 >>> 24) & 0x1;\n        out[op++] = (in0 >>> 25) & 0x1;\n        out[op++] = (in0 >>> 26) & 0x1;\n        out[op++] = (in0 >>> 27) & 0x1;\n        out[op++] = (in0 >>> 28) & 0x1;\n        out[op++] = (in0 >>> 29) & 0x1;\n        out[op++] = (in0 >>> 30) & 0x1;\n        out[op++] = (in0 >>> 31) & 0x1;\n    }\n}\n\nexport function fastUnpack256_2(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x3;\n        out[op++] = (in0 >>> 2) & 0x3;\n        out[op++] = (in0 >>> 4) & 0x3;\n        out[op++] = (in0 >>> 6) & 0x3;\n        out[op++] = (in0 >>> 8) & 0x3;\n        out[op++] = (in0 >>> 10) & 0x3;\n        out[op++] = (in0 >>> 12) & 0x3;\n        out[op++] = (in0 >>> 14) & 0x3;\n        out[op++] = (in0 >>> 16) & 0x3;\n        out[op++] = (in0 >>> 18) & 0x3;\n        out[op++] = (in0 >>> 20) & 0x3;\n        out[op++] = (in0 >>> 22) & 0x3;\n        out[op++] = (in0 >>> 24) & 0x3;\n        out[op++] = (in0 >>> 26) & 0x3;\n        out[op++] = (in0 >>> 28) & 0x3;\n        out[op++] = (in0 >>> 30) & 0x3;\n        out[op++] = (in1 >>> 0) & 0x3;\n        out[op++] = (in1 >>> 2) & 0x3;\n        out[op++] = (in1 >>> 4) & 0x3;\n        out[op++] = (in1 >>> 6) & 0x3;\n        out[op++] = (in1 >>> 8) & 0x3;\n        out[op++] = (in1 >>> 10) & 0x3;\n        out[op++] = (in1 >>> 12) & 0x3;\n        out[op++] = (in1 >>> 14) & 0x3;\n        out[op++] = (in1 >>> 16) & 0x3;\n        out[op++] = (in1 >>> 18) & 0x3;\n        out[op++] = (in1 >>> 20) & 0x3;\n        out[op++] = (in1 >>> 22) & 0x3;\n        out[op++] = (in1 >>> 24) & 0x3;\n        out[op++] = (in1 >>> 26) & 0x3;\n        out[op++] = (in1 >>> 28) & 0x3;\n        out[op++] = (in1 >>> 30) & 0x3;\n    }\n}\n\nexport function fastUnpack256_3(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x7;\n        out[op++] = (in0 >>> 3) & 0x7;\n        out[op++] = (in0 >>> 6) & 0x7;\n        out[op++] = (in0 >>> 9) & 0x7;\n        out[op++] = (in0 >>> 12) & 0x7;\n        out[op++] = (in0 >>> 15) & 0x7;\n        out[op++] = (in0 >>> 18) & 0x7;\n        out[op++] = (in0 >>> 21) & 0x7;\n        out[op++] = (in0 >>> 24) & 0x7;\n        out[op++] = (in0 >>> 27) & 0x7;\n        out[op++] = ((in0 >>> 30) | ((in1 & 0x1) << 2)) & 0x7;\n        out[op++] = (in1 >>> 1) & 0x7;\n        out[op++] = (in1 >>> 4) & 0x7;\n        out[op++] = (in1 >>> 7) & 0x7;\n        out[op++] = (in1 >>> 10) & 0x7;\n        out[op++] = (in1 >>> 13) & 0x7;\n        out[op++] = (in1 >>> 16) & 0x7;\n        out[op++] = (in1 >>> 19) & 0x7;\n        out[op++] = (in1 >>> 22) & 0x7;\n        out[op++] = (in1 >>> 25) & 0x7;\n        out[op++] = (in1 >>> 28) & 0x7;\n        out[op++] = ((in1 >>> 31) | ((in2 & 0x3) << 1)) & 0x7;\n        out[op++] = (in2 >>> 2) & 0x7;\n        out[op++] = (in2 >>> 5) & 0x7;\n        out[op++] = (in2 >>> 8) & 0x7;\n        out[op++] = (in2 >>> 11) & 0x7;\n        out[op++] = (in2 >>> 14) & 0x7;\n        out[op++] = (in2 >>> 17) & 0x7;\n        out[op++] = (in2 >>> 20) & 0x7;\n        out[op++] = (in2 >>> 23) & 0x7;\n        out[op++] = (in2 >>> 26) & 0x7;\n        out[op++] = (in2 >>> 29) & 0x7;\n    }\n}\n\nexport function fastUnpack256_4(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        const in3 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0xf;\n        out[op++] = (in0 >>> 4) & 0xf;\n        out[op++] = (in0 >>> 8) & 0xf;\n        out[op++] = (in0 >>> 12) & 0xf;\n        out[op++] = (in0 >>> 16) & 0xf;\n        out[op++] = (in0 >>> 20) & 0xf;\n        out[op++] = (in0 >>> 24) & 0xf;\n        out[op++] = (in0 >>> 28) & 0xf;\n        out[op++] = (in1 >>> 0) & 0xf;\n        out[op++] = (in1 >>> 4) & 0xf;\n        out[op++] = (in1 >>> 8) & 0xf;\n        out[op++] = (in1 >>> 12) & 0xf;\n        out[op++] = (in1 >>> 16) & 0xf;\n        out[op++] = (in1 >>> 20) & 0xf;\n        out[op++] = (in1 >>> 24) & 0xf;\n        out[op++] = (in1 >>> 28) & 0xf;\n        out[op++] = (in2 >>> 0) & 0xf;\n        out[op++] = (in2 >>> 4) & 0xf;\n        out[op++] = (in2 >>> 8) & 0xf;\n        out[op++] = (in2 >>> 12) & 0xf;\n        out[op++] = (in2 >>> 16) & 0xf;\n        out[op++] = (in2 >>> 20) & 0xf;\n        out[op++] = (in2 >>> 24) & 0xf;\n        out[op++] = (in2 >>> 28) & 0xf;\n        out[op++] = (in3 >>> 0) & 0xf;\n        out[op++] = (in3 >>> 4) & 0xf;\n        out[op++] = (in3 >>> 8) & 0xf;\n        out[op++] = (in3 >>> 12) & 0xf;\n        out[op++] = (in3 >>> 16) & 0xf;\n        out[op++] = (in3 >>> 20) & 0xf;\n        out[op++] = (in3 >>> 24) & 0xf;\n        out[op++] = (in3 >>> 28) & 0xf;\n    }\n}\n\nexport function fastUnpack256_5(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        const in3 = inValues[ip++] >>> 0;\n        const in4 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x1f;\n        out[op++] = (in0 >>> 5) & 0x1f;\n        out[op++] = (in0 >>> 10) & 0x1f;\n        out[op++] = (in0 >>> 15) & 0x1f;\n        out[op++] = (in0 >>> 20) & 0x1f;\n        out[op++] = (in0 >>> 25) & 0x1f;\n        out[op++] = ((in0 >>> 30) | ((in1 & 0x7) << 2)) & 0x1f;\n        out[op++] = (in1 >>> 3) & 0x1f;\n        out[op++] = (in1 >>> 8) & 0x1f;\n        out[op++] = (in1 >>> 13) & 0x1f;\n        out[op++] = (in1 >>> 18) & 0x1f;\n        out[op++] = (in1 >>> 23) & 0x1f;\n        out[op++] = ((in1 >>> 28) | ((in2 & 0x1) << 4)) & 0x1f;\n        out[op++] = (in2 >>> 1) & 0x1f;\n        out[op++] = (in2 >>> 6) & 0x1f;\n        out[op++] = (in2 >>> 11) & 0x1f;\n        out[op++] = (in2 >>> 16) & 0x1f;\n        out[op++] = (in2 >>> 21) & 0x1f;\n        out[op++] = (in2 >>> 26) & 0x1f;\n        out[op++] = ((in2 >>> 31) | ((in3 & 0xf) << 1)) & 0x1f;\n        out[op++] = (in3 >>> 4) & 0x1f;\n        out[op++] = (in3 >>> 9) & 0x1f;\n        out[op++] = (in3 >>> 14) & 0x1f;\n        out[op++] = (in3 >>> 19) & 0x1f;\n        out[op++] = (in3 >>> 24) & 0x1f;\n        out[op++] = ((in3 >>> 29) | ((in4 & 0x3) << 3)) & 0x1f;\n        out[op++] = (in4 >>> 2) & 0x1f;\n        out[op++] = (in4 >>> 7) & 0x1f;\n        out[op++] = (in4 >>> 12) & 0x1f;\n        out[op++] = (in4 >>> 17) & 0x1f;\n        out[op++] = (in4 >>> 22) & 0x1f;\n        out[op++] = (in4 >>> 27) & 0x1f;\n    }\n}\n\nexport function fastUnpack256_6(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        const in3 = inValues[ip++] >>> 0;\n        const in4 = inValues[ip++] >>> 0;\n        const in5 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x3f;\n        out[op++] = (in0 >>> 6) & 0x3f;\n        out[op++] = (in0 >>> 12) & 0x3f;\n        out[op++] = (in0 >>> 18) & 0x3f;\n        out[op++] = (in0 >>> 24) & 0x3f;\n        out[op++] = ((in0 >>> 30) | ((in1 & 0xf) << 2)) & 0x3f;\n        out[op++] = (in1 >>> 4) & 0x3f;\n        out[op++] = (in1 >>> 10) & 0x3f;\n        out[op++] = (in1 >>> 16) & 0x3f;\n        out[op++] = (in1 >>> 22) & 0x3f;\n        out[op++] = ((in1 >>> 28) | ((in2 & 0x3) << 4)) & 0x3f;\n        out[op++] = (in2 >>> 2) & 0x3f;\n        out[op++] = (in2 >>> 8) & 0x3f;\n        out[op++] = (in2 >>> 14) & 0x3f;\n        out[op++] = (in2 >>> 20) & 0x3f;\n        out[op++] = (in2 >>> 26) & 0x3f;\n        out[op++] = (in3 >>> 0) & 0x3f;\n        out[op++] = (in3 >>> 6) & 0x3f;\n        out[op++] = (in3 >>> 12) & 0x3f;\n        out[op++] = (in3 >>> 18) & 0x3f;\n        out[op++] = (in3 >>> 24) & 0x3f;\n        out[op++] = ((in3 >>> 30) | ((in4 & 0xf) << 2)) & 0x3f;\n        out[op++] = (in4 >>> 4) & 0x3f;\n        out[op++] = (in4 >>> 10) & 0x3f;\n        out[op++] = (in4 >>> 16) & 0x3f;\n        out[op++] = (in4 >>> 22) & 0x3f;\n        out[op++] = ((in4 >>> 28) | ((in5 & 0x3) << 4)) & 0x3f;\n        out[op++] = (in5 >>> 2) & 0x3f;\n        out[op++] = (in5 >>> 8) & 0x3f;\n        out[op++] = (in5 >>> 14) & 0x3f;\n        out[op++] = (in5 >>> 20) & 0x3f;\n        out[op++] = (in5 >>> 26) & 0x3f;\n    }\n}\n\nexport function fastUnpack256_7(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        const in3 = inValues[ip++] >>> 0;\n        const in4 = inValues[ip++] >>> 0;\n        const in5 = inValues[ip++] >>> 0;\n        const in6 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0x7f;\n        out[op++] = (in0 >>> 7) & 0x7f;\n        out[op++] = (in0 >>> 14) & 0x7f;\n        out[op++] = (in0 >>> 21) & 0x7f;\n        out[op++] = ((in0 >>> 28) | ((in1 & 0x7) << 4)) & 0x7f;\n        out[op++] = (in1 >>> 3) & 0x7f;\n        out[op++] = (in1 >>> 10) & 0x7f;\n        out[op++] = (in1 >>> 17) & 0x7f;\n        out[op++] = (in1 >>> 24) & 0x7f;\n        out[op++] = ((in1 >>> 31) | ((in2 & 0x3f) << 1)) & 0x7f;\n        out[op++] = (in2 >>> 6) & 0x7f;\n        out[op++] = (in2 >>> 13) & 0x7f;\n        out[op++] = (in2 >>> 20) & 0x7f;\n        out[op++] = ((in2 >>> 27) | ((in3 & 0x3) << 5)) & 0x7f;\n        out[op++] = (in3 >>> 2) & 0x7f;\n        out[op++] = (in3 >>> 9) & 0x7f;\n        out[op++] = (in3 >>> 16) & 0x7f;\n        out[op++] = (in3 >>> 23) & 0x7f;\n        out[op++] = ((in3 >>> 30) | ((in4 & 0x1f) << 2)) & 0x7f;\n        out[op++] = (in4 >>> 5) & 0x7f;\n        out[op++] = (in4 >>> 12) & 0x7f;\n        out[op++] = (in4 >>> 19) & 0x7f;\n        out[op++] = ((in4 >>> 26) | ((in5 & 0x1) << 6)) & 0x7f;\n        out[op++] = (in5 >>> 1) & 0x7f;\n        out[op++] = (in5 >>> 8) & 0x7f;\n        out[op++] = (in5 >>> 15) & 0x7f;\n        out[op++] = (in5 >>> 22) & 0x7f;\n        out[op++] = ((in5 >>> 29) | ((in6 & 0xf) << 3)) & 0x7f;\n        out[op++] = (in6 >>> 4) & 0x7f;\n        out[op++] = (in6 >>> 11) & 0x7f;\n        out[op++] = (in6 >>> 18) & 0x7f;\n        out[op++] = (in6 >>> 25) & 0x7f;\n    }\n}\n\nexport function fastUnpack256_8(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let c = 0; c < 8; c++) {\n        const in0 = inValues[ip++] >>> 0;\n        const in1 = inValues[ip++] >>> 0;\n        const in2 = inValues[ip++] >>> 0;\n        const in3 = inValues[ip++] >>> 0;\n        const in4 = inValues[ip++] >>> 0;\n        const in5 = inValues[ip++] >>> 0;\n        const in6 = inValues[ip++] >>> 0;\n        const in7 = inValues[ip++] >>> 0;\n        out[op++] = (in0 >>> 0) & 0xff;\n        out[op++] = (in0 >>> 8) & 0xff;\n        out[op++] = (in0 >>> 16) & 0xff;\n        out[op++] = (in0 >>> 24) & 0xff;\n        out[op++] = (in1 >>> 0) & 0xff;\n        out[op++] = (in1 >>> 8) & 0xff;\n        out[op++] = (in1 >>> 16) & 0xff;\n        out[op++] = (in1 >>> 24) & 0xff;\n        out[op++] = (in2 >>> 0) & 0xff;\n        out[op++] = (in2 >>> 8) & 0xff;\n        out[op++] = (in2 >>> 16) & 0xff;\n        out[op++] = (in2 >>> 24) & 0xff;\n        out[op++] = (in3 >>> 0) & 0xff;\n        out[op++] = (in3 >>> 8) & 0xff;\n        out[op++] = (in3 >>> 16) & 0xff;\n        out[op++] = (in3 >>> 24) & 0xff;\n        out[op++] = (in4 >>> 0) & 0xff;\n        out[op++] = (in4 >>> 8) & 0xff;\n        out[op++] = (in4 >>> 16) & 0xff;\n        out[op++] = (in4 >>> 24) & 0xff;\n        out[op++] = (in5 >>> 0) & 0xff;\n        out[op++] = (in5 >>> 8) & 0xff;\n        out[op++] = (in5 >>> 16) & 0xff;\n        out[op++] = (in5 >>> 24) & 0xff;\n        out[op++] = (in6 >>> 0) & 0xff;\n        out[op++] = (in6 >>> 8) & 0xff;\n        out[op++] = (in6 >>> 16) & 0xff;\n        out[op++] = (in6 >>> 24) & 0xff;\n        out[op++] = (in7 >>> 0) & 0xff;\n        out[op++] = (in7 >>> 8) & 0xff;\n        out[op++] = (in7 >>> 16) & 0xff;\n        out[op++] = (in7 >>> 24) & 0xff;\n    }\n}\n\nexport function fastUnpack256_16(inValues: Uint32Array, inPos: number, out: Uint32Array, outPos: number): void {\n    let op = outPos;\n    let ip = inPos;\n    for (let i = 0; i < 128; i++) {\n        const in0 = inValues[ip++] >>> 0;\n        out[op++] = in0 & 0xffff;\n        out[op++] = (in0 >>> 16) & 0xffff;\n    }\n}\n\nexport function fastUnpack256_Generic(\n    inValues: Uint32Array,\n    inPos: number,\n    out: Uint32Array,\n    outPos: number,\n    bitWidth: number,\n): void {\n    const mask = MASKS[bitWidth] >>> 0;\n\n    let inputWordIndex = inPos;\n    let bitOffset = 0;\n    let currentWord = inValues[inputWordIndex] >>> 0;\n    let op = outPos;\n\n    for (let c = 0; c < 8; c++) {\n        for (let i = 0; i < 32; i++) {\n            if (bitOffset + bitWidth <= 32) {\n                const value = (currentWord >>> bitOffset) & mask;\n                out[op + i] = value | 0;\n                bitOffset += bitWidth;\n\n                if (bitOffset === 32) {\n                    bitOffset = 0;\n                    inputWordIndex++;\n                    if (i !== 31) {\n                        currentWord = inValues[inputWordIndex] >>> 0;\n                    }\n                }\n            } else {\n                const lowBits = 32 - bitOffset;\n                const low = currentWord >>> bitOffset;\n\n                inputWordIndex++;\n                currentWord = inValues[inputWordIndex] >>> 0;\n\n                const highBits = bitWidth - lowBits;\n                const highMask = (-1 >>> (32 - highBits)) >>> 0;\n\n                const high = currentWord & highMask;\n\n                const value = (low | (high << lowBits)) & mask;\n                out[op + i] = value | 0;\n                bitOffset = highBits;\n            }\n        }\n        op += 32;\n\n        bitOffset = 0;\n        if (c < 7) {\n            currentWord = inValues[inputWordIndex] >>> 0;\n        }\n    }\n}\n"
  },
  {
    "path": "ts/src/decoding/fsstDecoder.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { decodeFsst } from \"./fsstDecoder\";\nimport { encodeFsst, createSymbolTable } from \"../encoding/fsstEncoder\";\n\nconst textEncoder = new TextEncoder();\n\ndescribe(\"decodeFsst\", () => {\n    describe(\"basic functionality\", () => {\n        it(\"should decode FSST compressed string data\", () => {\n            const inputString = \"HelloWorld !\";\n            const originalBytes = textEncoder.encode(inputString);\n\n            const { symbols, symbolLengths } = createSymbolTable([\"Hello\", \"World\", \"!\"]);\n            const encoded = encodeFsst(symbols, symbolLengths, originalBytes);\n            const decoded = decodeFsst(symbols, symbolLengths, encoded);\n\n            expect(decoded).toEqual(originalBytes);\n            expect(new TextDecoder().decode(decoded)).toBe(inputString);\n        });\n\n        it(\"should handle empty string\", () => {\n            const inputString = \"\";\n            const originalBytes = textEncoder.encode(inputString);\n\n            const { symbols, symbolLengths } = createSymbolTable([\"A\"]);\n            const encoded = encodeFsst(symbols, symbolLengths, originalBytes);\n            const decoded = decodeFsst(symbols, symbolLengths, encoded);\n\n            expect(decoded).toEqual(originalBytes);\n            expect(decoded.length).toBe(0);\n        });\n\n        it(\"should handle string with no matching symbols\", () => {\n            const inputString = \"12345\";\n            const originalBytes = textEncoder.encode(inputString);\n\n            const { symbols, symbolLengths } = createSymbolTable([\"abc\", \"def\", \"xyz\"]);\n            const encoded = encodeFsst(symbols, symbolLengths, originalBytes);\n            const decoded = decodeFsst(symbols, symbolLengths, encoded);\n\n            expect(decoded).toEqual(originalBytes);\n            expect(new TextDecoder().decode(decoded)).toBe(inputString);\n        });\n\n        it(\"should handle string with all matching symbols\", () => {\n            const inputString = \"AAAA\";\n            const originalBytes = textEncoder.encode(inputString);\n\n            const { symbols, symbolLengths } = createSymbolTable([\"A\"]);\n            const encoded = encodeFsst(symbols, symbolLengths, originalBytes);\n            const decoded = decodeFsst(symbols, symbolLengths, encoded);\n\n            expect(decoded).toEqual(originalBytes);\n            expect(new TextDecoder().decode(decoded)).toBe(inputString);\n        });\n    });\n\n    describe(\"compression verification\", () => {\n        it(\"should handle repeated strings efficiently\", () => {\n            const inputString = \"HelloWorldHelloWorld\";\n            const originalBytes = textEncoder.encode(inputString);\n\n            const { symbols, symbolLengths } = createSymbolTable([\"Hello\", \"World\"]);\n            const encoded = encodeFsst(symbols, symbolLengths, originalBytes);\n            const decoded = decodeFsst(symbols, symbolLengths, encoded);\n\n            expect(decoded).toEqual(originalBytes);\n            expect(new TextDecoder().decode(decoded)).toBe(inputString);\n            expect(encoded.length).toBeLessThan(originalBytes.length);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/fsstDecoder.ts",
    "content": "/**\n * Decode FSST compressed data\n *\n * @param symbols           Array of symbols, where each symbol can be between 1 and 8 bytes\n * @param symbolLengths     Array of symbol lengths, length of each symbol in symbols array\n * @param compressedData    FSST Compressed data, where each entry is an index to the symbols array\n * @returns                 Decoded data as Uint8Array\n */\n//TODO: improve -> quick and dirty implementation\nexport function decodeFsst(symbols: Uint8Array, symbolLengths: Uint32Array, compressedData: Uint8Array): Uint8Array {\n    //TODO: use typed array directly\n    const decodedData: number[] = [];\n    const symbolOffsets: number[] = new Array(symbolLengths.length).fill(0);\n\n    for (let i = 1; i < symbolLengths.length; i++) {\n        symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];\n    }\n\n    for (let i = 0; i < compressedData.length; i++) {\n        if (compressedData[i] === 255) {\n            decodedData.push(compressedData[++i]);\n        } else {\n            const symbolLength = symbolLengths[compressedData[i]];\n            const symbolOffset = symbolOffsets[compressedData[i]];\n            for (let j = 0; j < symbolLength; j++) {\n                decodedData.push(symbols[symbolOffset + j]);\n            }\n        }\n    }\n    return new Uint8Array(decodedData);\n}\n"
  },
  {
    "path": "ts/src/decoding/geometryDecoder.ts",
    "content": "import { decodeStreamMetadata, type MortonEncodedStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport type IntWrapper from \"./intWrapper\";\nimport {\n    decodeSignedInt32Stream,\n    decodeLengthStreamToOffsetBuffer,\n    decodeUnsignedConstInt32Stream,\n    decodeUnsignedInt32Stream,\n    getVectorType,\n} from \"./integerStreamDecoder\";\nimport { VectorType } from \"../vector/vectorType\";\nimport { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { LengthType } from \"../metadata/tile/lengthType\";\nimport { DictionaryType } from \"../metadata/tile/dictionaryType\";\nimport {\n    createConstGeometryVector,\n    createMortonEncodedConstGeometryVector,\n} from \"../vector/geometry/constGeometryVector\";\nimport { createFlatGeometryVector, createFlatGeometryVectorMortonEncoded } from \"../vector/geometry/flatGeometryVector\";\nimport { OffsetType } from \"../metadata/tile/offsetType\";\nimport { createConstGpuVector } from \"../vector/geometry/constGpuVector\";\nimport { createFlatGpuVector } from \"../vector/geometry/flatGpuVector\";\nimport type { GeometryVector, MortonSettings } from \"../vector/geometry/geometryVector\";\nimport type { GpuVector } from \"../vector/geometry/gpuVector\";\nimport type GeometryScaling from \"./geometryScaling\";\n\n// TODO: get rid of numFeatures parameter\nexport function decodeGeometryColumn(\n    tile: Uint8Array,\n    numStreams: number,\n    offset: IntWrapper,\n    numFeatures: number,\n    scalingData?: GeometryScaling,\n): GeometryVector | GpuVector {\n    const geometryTypeMetadata = decodeStreamMetadata(tile, offset);\n    const geometryTypesVectorType = getVectorType(geometryTypeMetadata, numFeatures, tile, offset);\n\n    let vertexOffsets: Uint32Array | undefined;\n    let vertexBuffer: Int32Array | Uint32Array | undefined;\n    let mortonSettings: MortonSettings | undefined;\n    let indexBuffer: Uint32Array | undefined;\n\n    if (geometryTypesVectorType === VectorType.CONST) {\n        /* All geometries in the column have the same geometry type */\n        const geometryType = decodeUnsignedConstInt32Stream(tile, offset, geometryTypeMetadata);\n\n        // Variables for const geometry path (directly decoded as offsets)\n        let geometryOffsets: Uint32Array | undefined;\n        let partOffsets: Uint32Array | undefined;\n        let ringOffsets: Uint32Array | undefined;\n        //TODO: use geometryOffsets for that? -> but then tessellated polygons can't be used with normal polygons\n        // in one FeatureTable?\n        let triangleOffsets: Uint32Array | undefined;\n\n        for (let i = 0; i < numStreams - 1; i++) {\n            const geometryStreamMetadata = decodeStreamMetadata(tile, offset);\n            switch (geometryStreamMetadata.physicalStreamType) {\n                case PhysicalStreamType.LENGTH:\n                    switch (geometryStreamMetadata.logicalStreamType.lengthType) {\n                        case LengthType.GEOMETRIES:\n                            geometryOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);\n                            break;\n                        case LengthType.PARTS:\n                            partOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);\n                            break;\n                        case LengthType.RINGS:\n                            ringOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);\n                            break;\n                        case LengthType.TRIANGLES:\n                            triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);\n                    }\n                    break;\n                case PhysicalStreamType.OFFSET: {\n                    switch (geometryStreamMetadata.logicalStreamType.offsetType) {\n                        case OffsetType.VERTEX:\n                            vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                            break;\n                        case OffsetType.INDEX:\n                            indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                            break;\n                    }\n                    break;\n                }\n                case PhysicalStreamType.DATA: {\n                    if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) {\n                        vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);\n                    } else {\n                        const mortonMetadata = geometryStreamMetadata as MortonEncodedStreamMetadata;\n                        mortonSettings = {\n                            numBits: mortonMetadata.numBits,\n                            coordinateShift: mortonMetadata.coordinateShift,\n                        };\n                        vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);\n                    }\n                    break;\n                }\n            }\n        }\n\n        if (indexBuffer) {\n            if (geometryOffsets !== undefined || partOffsets !== undefined) {\n                /* Case when the indices of a Polygon outline are encoded in the tile */\n                const topologyVector = { geometryOffsets, partOffsets, ringOffsets };\n                return createConstGpuVector(\n                    numFeatures,\n                    geometryType,\n                    triangleOffsets,\n                    indexBuffer,\n                    vertexBuffer,\n                    topologyVector,\n                );\n            }\n\n            /* Case when the no Polygon outlines are encoded in the tile */\n            return createConstGpuVector(numFeatures, geometryType, triangleOffsets, indexBuffer, vertexBuffer);\n        }\n\n        return mortonSettings === undefined\n            ? /* Currently only 2D coordinates (Vec2) are implemented in the encoder  */\n              createConstGeometryVector(\n                  numFeatures,\n                  geometryType,\n                  { geometryOffsets, partOffsets, ringOffsets },\n                  vertexOffsets,\n                  vertexBuffer,\n              )\n            : createMortonEncodedConstGeometryVector(\n                  numFeatures,\n                  geometryType,\n                  { geometryOffsets, partOffsets, ringOffsets },\n                  vertexOffsets,\n                  vertexBuffer,\n                  mortonSettings,\n              );\n    }\n\n    /* Different geometry types are mixed in the geometry column */\n    const geometryTypeVector = decodeUnsignedInt32Stream(tile, offset, geometryTypeMetadata);\n\n    // Variables for flat geometry path (decoded as lengths, then converted to offsets)\n    let geometryLengths: Uint32Array | undefined;\n    let partLengths: Uint32Array | undefined;\n    let ringLengths: Uint32Array | undefined;\n    //TODO: use geometryOffsets for that? -> but then tessellated polygons can't be used with normal polygons\n    // in one FeatureTable?\n    let triangleOffsets: Uint32Array | undefined;\n\n    for (let i = 0; i < numStreams - 1; i++) {\n        const geometryStreamMetadata = decodeStreamMetadata(tile, offset);\n        switch (geometryStreamMetadata.physicalStreamType) {\n            case PhysicalStreamType.LENGTH:\n                switch (geometryStreamMetadata.logicalStreamType.lengthType) {\n                    case LengthType.GEOMETRIES:\n                        geometryLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                        break;\n                    case LengthType.PARTS:\n                        partLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                        break;\n                    case LengthType.RINGS:\n                        ringLengths = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                        break;\n                    case LengthType.TRIANGLES:\n                        triangleOffsets = decodeLengthStreamToOffsetBuffer(tile, offset, geometryStreamMetadata);\n                }\n                break;\n            case PhysicalStreamType.OFFSET:\n                switch (geometryStreamMetadata.logicalStreamType.offsetType) {\n                    case OffsetType.VERTEX:\n                        vertexOffsets = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                        break;\n                    case OffsetType.INDEX:\n                        indexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata);\n                        break;\n                }\n                break;\n            case PhysicalStreamType.DATA:\n                if (DictionaryType.VERTEX === geometryStreamMetadata.logicalStreamType.dictionaryType) {\n                    vertexBuffer = decodeSignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);\n                } else {\n                    const mortonMetadata = geometryStreamMetadata as MortonEncodedStreamMetadata;\n                    mortonSettings = {\n                        numBits: mortonMetadata.numBits,\n                        coordinateShift: mortonMetadata.coordinateShift,\n                    };\n                    vertexBuffer = decodeUnsignedInt32Stream(tile, offset, geometryStreamMetadata, scalingData);\n                }\n                break;\n        }\n    }\n\n    // TODO: refactor the following instructions -> decode in one pass for performance reasons\n    /* Calculate the offsets from the length buffer for util access */\n    let geometryOffsets: Uint32Array | undefined;\n    let partOffsets: Uint32Array | undefined;\n    let ringOffsets: Uint32Array | undefined;\n\n    if (geometryLengths) {\n        geometryOffsets = decodeRootLengthStream(geometryTypeVector, geometryLengths, 2);\n        if (partLengths && ringLengths) {\n            partOffsets = decodeLevel1LengthStream(geometryTypeVector, geometryOffsets, partLengths, false);\n            ringOffsets = decodeLevel2LengthStream(geometryTypeVector, geometryOffsets, partOffsets, ringLengths);\n        } else if (partLengths) {\n            partOffsets = decodeLevel1WithoutRingBufferLengthStream(geometryTypeVector, geometryOffsets, partLengths);\n        }\n    } else if (partLengths && ringLengths) {\n        partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 1);\n        ringOffsets = decodeLevel1LengthStream(geometryTypeVector, partOffsets, ringLengths, true);\n    } else if (partLengths) {\n        partOffsets = decodeRootLengthStream(geometryTypeVector, partLengths, 0);\n    }\n\n    if (indexBuffer && !partOffsets) {\n        /* Case when the indices of a Polygon outline are not encoded in the data so no\n         *  topology data are present in the tile */\n        return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer);\n    }\n\n    if (indexBuffer) {\n        /* Case when the indices of a Polygon outline are encoded in the tile */\n        return createFlatGpuVector(geometryTypeVector, triangleOffsets, indexBuffer, vertexBuffer, {\n            geometryOffsets,\n            partOffsets,\n            ringOffsets,\n        });\n    }\n\n    return mortonSettings === undefined /* Currently only 2D coordinates (Vec2) are implemented in the encoder  */\n        ? createFlatGeometryVector(\n              geometryTypeVector,\n              { geometryOffsets, partOffsets, ringOffsets },\n              vertexOffsets,\n              vertexBuffer,\n          )\n        : createFlatGeometryVectorMortonEncoded(\n              geometryTypeVector,\n              { geometryOffsets, partOffsets, ringOffsets },\n              vertexOffsets,\n              vertexBuffer,\n              mortonSettings,\n          );\n}\n\n/*\n * Handle the parsing of the different topology length buffers separate not generic to reduce the\n * branching and improve the performance\n */\nfunction decodeRootLengthStream(\n    geometryTypes: Uint32Array,\n    rootLengthStream: Uint32Array,\n    bufferId: number,\n): Uint32Array {\n    const rootBufferOffsets = new Uint32Array(geometryTypes.length + 1);\n    let previousOffset = 0;\n    rootBufferOffsets[0] = previousOffset;\n    let rootLengthCounter = 0;\n    for (let i = 0; i < geometryTypes.length; i++) {\n        /* Test if the geometry has and entry in the root buffer\n         * BufferId: 2 GeometryOffsets -> MultiPolygon, MultiLineString, MultiPoint\n         * BufferId: 1 PartOffsets -> Polygon\n         * BufferId: 0 PartOffsets, RingOffsets -> LineString\n         * */\n        previousOffset = rootBufferOffsets[i + 1] =\n            previousOffset + (geometryTypes[i] > bufferId ? rootLengthStream[rootLengthCounter++] : 1);\n    }\n\n    return rootBufferOffsets;\n}\n\nfunction decodeLevel1LengthStream(\n    geometryTypes: Uint32Array,\n    rootOffsetBuffer: Uint32Array,\n    level1LengthBuffer: Uint32Array,\n    isLineStringPresent: boolean,\n): Uint32Array {\n    const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);\n    let previousOffset = 0;\n    level1BufferOffsets[0] = previousOffset;\n    let level1BufferCounter = 1;\n    let level1LengthBufferCounter = 0;\n    for (let i = 0; i < geometryTypes.length; i++) {\n        const geometryType = geometryTypes[i];\n        const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n        if (\n            geometryType === 5 ||\n            geometryType === 2 ||\n            (isLineStringPresent && (geometryType === 4 || geometryType === 1))\n        ) {\n            /* For MultiPolygon, Polygon and in some cases for MultiLineString and LineString\n             * a value in the level1LengthBuffer exists */\n            for (let j = 0; j < numGeometries; j++) {\n                previousOffset = level1BufferOffsets[level1BufferCounter++] =\n                    previousOffset + level1LengthBuffer[level1LengthBufferCounter++];\n            }\n        } else {\n            /* For MultiPoint and Point and in some cases for MultiLineString and LineString no value in the\n             * level1LengthBuffer exists */\n            for (let j = 0; j < numGeometries; j++) {\n                level1BufferOffsets[level1BufferCounter++] = ++previousOffset;\n            }\n        }\n    }\n\n    return level1BufferOffsets;\n}\n\n/*\n * Case where no ring buffer exists so no MultiPolygon or Polygon geometry is part of the buffer\n */\nfunction decodeLevel1WithoutRingBufferLengthStream(\n    geometryTypes: Uint32Array,\n    rootOffsetBuffer: Uint32Array,\n    level1LengthBuffer: Uint32Array,\n): Uint32Array {\n    const level1BufferOffsets = new Uint32Array(rootOffsetBuffer[rootOffsetBuffer.length - 1] + 1);\n    let previousOffset = 0;\n    level1BufferOffsets[0] = previousOffset;\n    let level1OffsetBufferCounter = 1;\n    let level1LengthCounter = 0;\n    for (let i = 0; i < geometryTypes.length; i++) {\n        const geometryType = geometryTypes[i];\n        const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n        if (geometryType === 4 || geometryType === 1) {\n            /* For MultiLineString and LineString a value in the level1LengthBuffer exists */\n            for (let j = 0; j < numGeometries; j++) {\n                previousOffset = level1BufferOffsets[level1OffsetBufferCounter++] =\n                    previousOffset + level1LengthBuffer[level1LengthCounter++];\n            }\n        } else {\n            /* For MultiPoint and Point no value in level1LengthBuffer exists */\n            for (let j = 0; j < numGeometries; j++) {\n                level1BufferOffsets[level1OffsetBufferCounter++] = ++previousOffset;\n            }\n        }\n    }\n\n    return level1BufferOffsets;\n}\n\nfunction decodeLevel2LengthStream(\n    geometryTypes: Uint32Array,\n    rootOffsetBuffer: Uint32Array,\n    level1OffsetBuffer: Uint32Array,\n    level2LengthBuffer: Uint32Array,\n): Uint32Array {\n    const level2BufferOffsets = new Uint32Array(level1OffsetBuffer[level1OffsetBuffer.length - 1] + 1);\n    let previousOffset = 0;\n    level2BufferOffsets[0] = previousOffset;\n    let level1OffsetBufferCounter = 1;\n    let level2OffsetBufferCounter = 1;\n    let level2LengthBufferCounter = 0;\n    for (let i = 0; i < geometryTypes.length; i++) {\n        const geometryType = geometryTypes[i];\n        const numGeometries = rootOffsetBuffer[i + 1] - rootOffsetBuffer[i];\n        if (geometryType !== 0 && geometryType !== 3) {\n            /* For MultiPolygon, MultiLineString, Polygon and LineString a value in level2LengthBuffer\n             * exists */\n            for (let j = 0; j < numGeometries; j++) {\n                const numParts =\n                    level1OffsetBuffer[level1OffsetBufferCounter] - level1OffsetBuffer[level1OffsetBufferCounter - 1];\n                level1OffsetBufferCounter++;\n                for (let k = 0; k < numParts; k++) {\n                    previousOffset = level2BufferOffsets[level2OffsetBufferCounter++] =\n                        previousOffset + level2LengthBuffer[level2LengthBufferCounter++];\n                }\n            }\n        } else {\n            /* For MultiPoint and Point no value in level2LengthBuffer exists */\n            for (let j = 0; j < numGeometries; j++) {\n                level2BufferOffsets[level2OffsetBufferCounter++] = ++previousOffset;\n                level1OffsetBufferCounter++;\n            }\n        }\n    }\n\n    return level2BufferOffsets;\n}\n"
  },
  {
    "path": "ts/src/decoding/geometryScaling.ts",
    "content": "export default interface GeometryScaling {\n    extent: number;\n    min: number;\n    max: number;\n    scale?: number;\n}\n"
  },
  {
    "path": "ts/src/decoding/intWrapper.ts",
    "content": "// Ported from https://github.com/lemire/JavaFastPFOR/blob/master/src/main/java/me/lemire/integercompression/IntWrapper.java\n\nexport default class IntWrapper {\n    constructor(private value: number) {}\n\n    public get(): number {\n        return this.value;\n    }\n\n    public set(v: number): void {\n        this.value = v;\n    }\n\n    public increment(): number {\n        return this.value++;\n    }\n\n    public add(v: number): void {\n        this.value += v;\n    }\n}\n"
  },
  {
    "path": "ts/src/decoding/integerDecodingUtils.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport {\n    createFastPforWireDecodeWorkspace,\n    decodeFastPfor,\n    decodeFastPforWithWorkspace,\n    decodeVarintInt32,\n    decodeVarintInt64,\n    decodeVarintFloat64,\n    decodeZigZagInt32,\n    decodeZigZagInt64,\n    decodeZigZagFloat64,\n    decodeZigZagInt32Value,\n    decodeZigZagInt64Value,\n    decodeUnsignedRleInt32,\n    decodeUnsignedRleInt64,\n    decodeUnsignedRleFloat64,\n    decodeZigZagDeltaInt64,\n    decodeDeltaRleInt32,\n    decodeDeltaRleInt64,\n    decodeUnsignedConstRleInt64,\n    decodeZigZagConstRleInt64,\n    decodeZigZagSequenceRleInt64,\n    decodeZigZagRleInt32,\n    decodeZigZagRleInt64,\n    decodeZigZagRleFloat64,\n    decodeZigZagRleDeltaInt32,\n    fastInverseDelta,\n    decodeZigZagSequenceRleInt32,\n    decodeZigZagDeltaInt32,\n    decodeZigZagDeltaFloat64,\n    decodeRleDeltaInt32,\n    decodeComponentwiseDeltaVec2,\n    decodeComponentwiseDeltaVec2Scaled,\n} from \"./integerDecodingUtils\";\nimport IntWrapper from \"./intWrapper\";\nimport {\n    encodeVarintInt32,\n    encodeVarintInt64,\n    encodeDeltaInt32,\n    encodeDeltaRleInt32,\n    encodeDeltaRleInt64,\n    encodeUnsignedRleFloat64,\n    encodeUnsignedRleInt32,\n    encodeUnsignedRleInt64,\n    encodeZigZagDeltaInt64,\n    encodeZigZagFloat64,\n    encodeZigZagInt32,\n    encodeZigZagInt32Value,\n    encodeZigZagInt64,\n    encodeZigZagInt64Value,\n    encodeZigZagRleFloat64,\n    encodeZigZagRleInt32,\n    encodeZigZagRleInt64,\n    encodeZigZagDeltaInt32,\n    encodeZigZagDeltaFloat64,\n    encodeVarintFloat64,\n    encodeZigZagRleDeltaInt32,\n    encodeRleDeltaInt32,\n    encodeComponentwiseDeltaVec2,\n    encodeComponentwiseDeltaVec2Scaled,\n} from \"../encoding/integerEncodingUtils\";\n\ndescribe(\"IntegerDecodingUtils\", () => {\n    describe(\"Varint decoding\", () => {\n        it(\"should decode Int32\", () => {\n            const value = 2 ** 10;\n            const encoded = encodeVarintInt32(new Uint32Array([value]));\n            const decoded = decodeVarintInt32(encoded, new IntWrapper(0), 1);\n            expect(decoded[0]).toEqual(value);\n        });\n\n        it(\"should decode Int64\", () => {\n            const value = 2n ** 50n;\n            const encoded = encodeVarintInt64(new BigUint64Array([value]));\n            const decoded = decodeVarintInt64(encoded, new IntWrapper(0), 1);\n            expect(decoded[0]).toEqual(value);\n        });\n\n        it(\"should return valid decoded values for varint long to float64\", () => {\n            const value = 2 ** 40;\n            const varintEncoded = encodeVarintFloat64(new Float64Array([value]));\n            const actualValues = decodeVarintFloat64(varintEncoded, new IntWrapper(0), 1);\n            expect(actualValues[0]).toEqual(value);\n        });\n    });\n\n    describe(\"ZigZag encoding\", () => {\n        it(\"should decode zigzag Int32Array\", () => {\n            const data = new Int32Array([0, 1, 2, 3]);\n            const encoded = encodeZigZagInt32(data);\n            const decoded = decodeZigZagInt32(encoded);\n            expect(Array.from(decoded)).toEqual([0, 1, 2, 3]);\n        });\n\n        it(\"should decode zigzag BigInt64Array\", () => {\n            const data = new BigInt64Array([0n, 1n, 2n, 3n]);\n            const encoded = encodeZigZagInt64(data);\n            const decoded = decodeZigZagInt64(encoded);\n            expect(Array.from(decoded)).toEqual([0n, 1n, 2n, 3n]);\n        });\n\n        it(\"should decode zigzag Float64Array\", () => {\n            const value = 2 ** 35;\n            const data = new Float64Array([value]);\n            encodeZigZagFloat64(data);\n            decodeZigZagFloat64(data);\n            expect(Array.from(data)).toEqual([value]);\n        });\n\n        it(\"should decode single Int32 zigzag values\", () => {\n            expect(encodeZigZagInt32Value(decodeZigZagInt32Value(0))).toBe(0);\n            expect(encodeZigZagInt32Value(decodeZigZagInt32Value(1))).toBe(1);\n            expect(encodeZigZagInt32Value(decodeZigZagInt32Value(2))).toBe(2);\n        });\n\n        it(\"should decode single BigInt zigzag values\", () => {\n            expect(encodeZigZagInt64Value(decodeZigZagInt64Value(0n))).toBe(0n);\n            expect(encodeZigZagInt64Value(decodeZigZagInt64Value(1n))).toBe(1n);\n        });\n    });\n\n    describe(\"RLE decoding\", () => {\n        describe(\"Unsigned RLE\", () => {\n            it(\"should decode empty unsigned RLE\", () => {\n                const data = new Uint32Array([]);\n                const encodedRle = encodeUnsignedRleInt32(data);\n                const decoded = decodeUnsignedRleInt32(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode unsigned RLE\", () => {\n                const data = new Uint32Array([10, 10, 20, 20, 20]);\n                const encodedRle = encodeUnsignedRleInt32(data);\n                const decoded = decodeUnsignedRleInt32(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([10, 10, 20, 20, 20]);\n            });\n\n            it(\"should decode empty unsigned RLE Int64\", () => {\n                const data = new BigInt64Array([]);\n                const encodedRle = encodeUnsignedRleInt64(data);\n                const decoded = decodeUnsignedRleInt64(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode unsigned RLE Int64\", () => {\n                const data = new BigInt64Array([10n, 10n, 20n, 20n, 20n]);\n                const encodedRle = encodeUnsignedRleInt64(data);\n                const decoded = decodeUnsignedRleInt64(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([10n, 10n, 20n, 20n, 20n]);\n            });\n\n            it(\"should decode empty unsigned RLE Float64\", () => {\n                const data = new Float64Array([]);\n                const encodedRle = encodeUnsignedRleFloat64(data);\n                const decoded = decodeUnsignedRleFloat64(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode unsigned RLE Float64\", () => {\n                const data = new Float64Array([10.5, 10.5, 20.5, 20.5, 20.5]);\n                const encodedRle = encodeUnsignedRleFloat64(data);\n                const decoded = decodeUnsignedRleFloat64(encodedRle.data, encodedRle.runs, data.length);\n                expect(Array.from(decoded)).toEqual([10.5, 10.5, 20.5, 20.5, 20.5]);\n            });\n        });\n\n        describe(\"ZigZag RLE\", () => {\n            it(\"should decode empty ZigZag RLE Int32\", () => {\n                const data = new Int32Array([]);\n                const encoded = encodeZigZagRleInt32(data);\n                const decoded = decodeZigZagRleInt32(encoded.data, encoded.runs, encoded.numTotalValues);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode ZigZag RLE Int32\", () => {\n                const encoded = new Int32Array([2, 2, 3, 3, 3]);\n                const encodedData = encodeZigZagRleInt32(encoded);\n                const decoded = decodeZigZagRleInt32(encodedData.data, encodedData.runs, encodedData.numTotalValues);\n                expect(Array.from(decoded)).toEqual([2, 2, 3, 3, 3]);\n            });\n\n            it(\"should decode empty ZigZag RLE Int64\", () => {\n                const data = new BigInt64Array([]);\n                const encoded = encodeZigZagRleInt64(data);\n                const decoded = decodeZigZagRleInt64(encoded.data, encoded.runs, encoded.numTotalValues);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode ZigZag RLE Int64\", () => {\n                const encoded = new BigInt64Array([2n, 2n, 3n, 3n, 3n]);\n                const encodedData = encodeZigZagRleInt64(encoded);\n                const decoded = decodeZigZagRleInt64(encodedData.data, encodedData.runs, encodedData.numTotalValues);\n                expect(Array.from(decoded)).toEqual([2n, 2n, 3n, 3n, 3n]);\n            });\n\n            it(\"should decode empty ZigZag RLE Float64\", () => {\n                const data = new Float64Array([]);\n                const encoded = encodeZigZagRleFloat64(data);\n                const decoded = decodeZigZagRleFloat64(encoded.data, encoded.runs, encoded.numTotalValues);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode ZigZag RLE Float64\", () => {\n                const encoded = new Float64Array([2, 2, 3, 3, 3]);\n                const encodedData = encodeZigZagRleFloat64(encoded);\n                const decoded = decodeZigZagRleFloat64(encodedData.data, encodedData.runs, encodedData.numTotalValues);\n                expect(Array.from(decoded)).toEqual([2, 2, 3, 3, 3]);\n            });\n        });\n    });\n\n    describe(\"Delta encoding\", () => {\n        describe(\"ZigZag Delta\", () => {\n            it(\"should decode zigzag delta Int32\", () => {\n                const data = new Int32Array([1, 2, 3, 5, 6, 7]);\n                const encoded = encodeZigZagDeltaInt32(data);\n                const decoded = decodeZigZagDeltaInt32(encoded);\n                expect(Array.from(decoded)).toEqual([1, 2, 3, 5, 6, 7]);\n            });\n\n            it(\"should decode zigzag delta Int64\", () => {\n                const data = new BigInt64Array([1n, 2n, 3n, 5n, 6n, 7n]);\n                const encoded = encodeZigZagDeltaInt64(data);\n                const decoded = decodeZigZagDeltaInt64(encoded);\n                expect(Array.from(decoded)).toEqual([1n, 2n, 3n, 5n, 6n, 7n]);\n            });\n\n            it(\"should decode zigzag delta Float64\", () => {\n                const data = new Float64Array([1.0, 2.0, 3.0, 5.0, 6.0, 7.0]);\n                encodeZigZagDeltaFloat64(data);\n                decodeZigZagDeltaFloat64(data);\n                expect(Array.from(data)).toEqual([1.0, 2.0, 3.0, 5.0, 6.0, 7.0]);\n            });\n        });\n\n        describe(\"Fast inverse delta\", () => {\n            it(\"should apply fast inverse delta\", () => {\n                const data = new Int32Array([10, 15, 18, 20]);\n                fastInverseDelta(data);\n                encodeDeltaInt32(data);\n                expect(Array.from(data)).toEqual([10, 15, 18, 20]);\n            });\n        });\n\n        describe(\"Componentwise Delta Vec2\", () => {\n            it(\"should decode empty array\", () => {\n                const data = new Int32Array([]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2(data);\n                const decoded = decodeComponentwiseDeltaVec2(encoded);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n\n            it(\"should decode single vertex\", () => {\n                const data = new Int32Array([10, 20]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2(data);\n                const decoded = decodeComponentwiseDeltaVec2(encoded);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n\n            it(\"should decode many vertices (unrolled loop test)\", () => {\n                const data = new Int32Array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2(data);\n                const decoded = decodeComponentwiseDeltaVec2(encoded);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n        });\n\n        describe(\"Componentwise Delta Vec2 Scaled\", () => {\n            const scale = 2.0;\n            const min = 0;\n            const max = 4096;\n\n            it(\"should decode empty array\", () => {\n                const data = new Int32Array([]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2Scaled(data, scale);\n                const decoded = decodeComponentwiseDeltaVec2Scaled(encoded, scale, min, max);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n\n            it(\"should decode single vertex\", () => {\n                const data = new Int32Array([100, 200]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2Scaled(data, scale);\n                const decoded = decodeComponentwiseDeltaVec2Scaled(encoded, scale, min, max);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n\n            it(\"should decode with different scale\", () => {\n                const testScale = 10.0;\n                const data = new Int32Array([1000, 2000, 1100, 2200]);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2Scaled(data, testScale);\n                const decoded = decodeComponentwiseDeltaVec2Scaled(encoded, testScale, min, max);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n\n            it(\"should decode many vertices (unrolled loop test)\", () => {\n                const numbers: number[] = [];\n                for (let i = 0; i < 100; i++) {\n                    numbers.push(i * 10, i * 10);\n                }\n                const data = new Int32Array(numbers);\n                const expected = new Int32Array(data);\n                const encoded = encodeComponentwiseDeltaVec2Scaled(data, scale);\n                const decoded = decodeComponentwiseDeltaVec2Scaled(encoded, scale, min, max);\n                expect(Array.from(decoded)).toEqual(Array.from(expected));\n            });\n        });\n\n        describe(\"Delta RLE\", () => {\n            it(\"should decode empty delta RLE Int32\", () => {\n                const data = new Int32Array([]);\n                const encoded = encodeDeltaRleInt32(data);\n                const decoded = decodeDeltaRleInt32(encoded.data, encoded.runs, encoded.numValues);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode delta RLE Int32\", () => {\n                const data = new Int32Array([1, 2, 3, 5, 6, 7]);\n                const encoded = encodeDeltaRleInt32(data);\n                const decoded = decodeDeltaRleInt32(encoded.data, encoded.runs, encoded.numValues);\n                expect(Array.from(decoded)).toEqual([1, 2, 3, 5, 6, 7]);\n            });\n\n            it(\"should decode empty delta RLE Int64\", () => {\n                const data = new BigInt64Array([]);\n                const encoded = encodeDeltaRleInt64(data);\n                const decoded = decodeDeltaRleInt64(encoded.data, encoded.runs, encoded.numValues);\n                expect(Array.from(decoded)).toEqual([]);\n            });\n\n            it(\"should decode delta RLE Int64\", () => {\n                const data = new BigInt64Array([1n, 2n, 3n, 5n, 6n, 7n]);\n                const encoded = encodeDeltaRleInt64(data);\n                const decoded = decodeDeltaRleInt64(encoded.data, encoded.runs, encoded.numValues);\n                expect(Array.from(decoded)).toEqual([1n, 2n, 3n, 5n, 6n, 7n]);\n            });\n        });\n\n        describe(\"ZigZag RLE Delta\", () => {\n            it(\"should decode zigzag RLE delta\", () => {\n                const data = new Int32Array([1, 2, 3, 4]);\n                const encoded = encodeZigZagRleDeltaInt32(data);\n                const decoded = decodeZigZagRleDeltaInt32(encoded.data, encoded.runs, encoded.numTotalValues);\n                // The decoder is adding a 0 at the start\n                expect(Array.from(decoded)).toEqual([0, 1, 2, 3, 4]);\n            });\n\n            it(\"should decode RLE delta\", () => {\n                const data = new Uint32Array([1, 2, 3, 4]);\n                const encoded = encodeRleDeltaInt32(data);\n                const decoded = decodeRleDeltaInt32(encoded.data, encoded.runs, encoded.numTotalValues);\n                // The decoder is adding a 0 at the start\n                expect(Array.from(decoded)).toEqual([0, 1, 2, 3, 4]);\n            });\n        });\n    });\n\n    describe(\"Const and Sequence RLE\", () => {\n        it(\"should decode unsigned const RLE Int64\", () => {\n            const data = new BigInt64Array([5n, 42n]);\n            expect(decodeUnsignedConstRleInt64(data)).toBe(42n);\n        });\n\n        it(\"should decode zigzag const RLE Int64\", () => {\n            const data = new BigInt64Array([5n, encodeZigZagInt64Value(2n)]);\n            expect(decodeZigZagConstRleInt64(data)).toBe(2n);\n        });\n\n        it(\"should decode zigzag sequence RLE Int32\", () => {\n            const data = new Int32Array([5, 2]);\n            const [base, delta] = decodeZigZagSequenceRleInt32(data);\n            expect(base).toBe(1);\n            expect(delta).toBe(1);\n        });\n\n        it(\"should decode zigzag sequence RLE Int32 with delta\", () => {\n            const data = new Int32Array([5, 2, 5, 2]);\n            const [base, delta] = decodeZigZagSequenceRleInt32(data);\n            expect(base).toBe(-3);\n            expect(delta).toBe(1);\n        });\n\n        it(\"should decode zigzag sequence RLE Int64\", () => {\n            const data = new BigInt64Array([5n, 2n]);\n            const [base, delta] = decodeZigZagSequenceRleInt64(data);\n            expect(base).toBe(1n);\n            expect(delta).toBe(1n);\n        });\n\n        it(\"should decode zigzag sequence RLE Int64 with delta\", () => {\n            const data = new BigInt64Array([5n, 2n, 5n, 2n]);\n            const [base, delta] = decodeZigZagSequenceRleInt64(data);\n            expect(base).toBe(-3n);\n            expect(delta).toBe(1n);\n        });\n    });\n\n    it(\"should reject FastPFOR byte lengths that are not multiple of 4\", () => {\n        const encoded = new Uint8Array([0x01, 0x02, 0x03]);\n        const offset = new IntWrapper(0);\n\n        expect(() => decodeFastPfor(encoded, 0, encoded.length, offset)).toThrow(/invalid encodedByteLength=3/);\n        expect(offset.get()).toBe(0);\n    });\n\n    it(\"should reject FastPFOR byte lengths with workspace API when not multiple of 4\", () => {\n        const encoded = new Uint8Array([0x01, 0x02, 0x03]);\n        const offset = new IntWrapper(0);\n        const workspace = createFastPforWireDecodeWorkspace();\n\n        expect(() => decodeFastPforWithWorkspace(encoded, 0, encoded.length, offset, workspace)).toThrow(\n            /invalid encodedByteLength=3/,\n        );\n        expect(offset.get()).toBe(0);\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/integerDecodingUtils.ts",
    "content": "import type IntWrapper from \"./intWrapper\";\nimport {\n    createFastPforWireDecodeWorkspace,\n    decodeFastPforInt32,\n    ensureFastPforWireEncodedWordsCapacity,\n    type FastPforWireDecodeWorkspace,\n} from \"./fastPforDecoder\";\nimport { decodeBigEndianInt32sInto } from \"./bigEndianDecode\";\nexport type { FastPforWireDecodeWorkspace } from \"./fastPforDecoder\";\nexport { createFastPforWireDecodeWorkspace } from \"./fastPforDecoder\";\n\n//based on https://github.com/mapbox/pbf/blob/main/index.js\nexport function decodeVarintInt32(buf: Uint8Array, bufferOffset: IntWrapper, numValues: number): Uint32Array {\n    const dst = new Uint32Array(numValues);\n    let dstOffset = 0;\n    let offset = bufferOffset.get();\n    for (let i = 0; i < dst.length; i++) {\n        let b = buf[offset++];\n        let val = b & 0x7f;\n        if (b < 0x80) {\n            dst[dstOffset++] = val;\n            continue;\n        }\n\n        b = buf[offset++];\n        val |= (b & 0x7f) << 7;\n        if (b < 0x80) {\n            dst[dstOffset++] = val;\n            continue;\n        }\n\n        b = buf[offset++];\n        val |= (b & 0x7f) << 14;\n        if (b < 0x80) {\n            dst[dstOffset++] = val;\n            continue;\n        }\n\n        b = buf[offset++];\n        val |= (b & 0x7f) << 21;\n        if (b < 0x80) {\n            dst[dstOffset++] = val;\n            continue;\n        }\n\n        b = buf[offset++];\n        val |= (b & 0x0f) << 28;\n        dst[dstOffset++] = val;\n    }\n\n    bufferOffset.set(offset);\n    return dst;\n}\n\nexport function decodeVarintInt64(src: Uint8Array, offset: IntWrapper, numValues: number): BigUint64Array {\n    const dst = new BigUint64Array(numValues);\n    for (let i = 0; i < dst.length; i++) {\n        dst[i] = decodeVarintInt64Value(src, offset);\n    }\n    return dst;\n}\n\n// Source: https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/util/VarInt.java\nfunction decodeVarintInt64Value(bytes: Uint8Array, pos: IntWrapper): bigint {\n    let value = 0n;\n    let shift = 0;\n    let index = pos.get();\n    while (index < bytes.length) {\n        const b = bytes[index++];\n        value |= BigInt(b & 0x7f) << BigInt(shift);\n        if ((b & 0x80) === 0) {\n            break;\n        }\n        shift += 7;\n        if (shift >= 64) {\n            throw new Error(\"Varint too long\");\n        }\n    }\n    pos.set(index);\n    return value;\n}\n\n/*\n * Since decoding Int64 values to BigInt is more than an order of magnitude slower in the tests then using a Float64,\n * this decoding method limits the max size of a Long value to 53 bits\n */\nexport function decodeVarintFloat64(src: Uint8Array, offset: IntWrapper, numValues: number): Float64Array {\n    const dst = new Float64Array(numValues);\n    for (let i = 0; i < numValues; i++) {\n        dst[i] = decodeVarintFloat64Value(src, offset);\n    }\n    return dst;\n}\n\n//based on https://github.com/mapbox/pbf/blob/main/index.js\nfunction decodeVarintFloat64Value(buf: Uint8Array, offset: IntWrapper): number {\n    let val;\n    let b;\n    b = buf[offset.get()];\n    offset.increment();\n    val = b & 0x7f;\n    if (b < 0x80) return val;\n    b = buf[offset.get()];\n    offset.increment();\n    val |= (b & 0x7f) << 7;\n    if (b < 0x80) return val;\n    b = buf[offset.get()];\n    offset.increment();\n    val |= (b & 0x7f) << 14;\n    if (b < 0x80) return val;\n    b = buf[offset.get()];\n    offset.increment();\n    val |= (b & 0x7f) << 21;\n    if (b < 0x80) return val;\n    b = buf[offset.get()];\n    val |= (b & 0x0f) << 28;\n\n    return decodeVarintRemainder(val, buf, offset);\n}\n\nfunction decodeVarintRemainder(l, buf, offset) {\n    let h;\n    let b;\n    b = buf[offset.get()];\n    offset.increment();\n    h = (b & 0x70) >> 4;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n    b = buf[offset.get()];\n    offset.increment();\n    h |= (b & 0x7f) << 3;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n    b = buf[offset.get()];\n    offset.increment();\n    h |= (b & 0x7f) << 10;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n    b = buf[offset.get()];\n    offset.increment();\n    h |= (b & 0x7f) << 17;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n    b = buf[offset.get()];\n    offset.increment();\n    h |= (b & 0x7f) << 24;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n    b = buf[offset.get()];\n    offset.increment();\n    h |= (b & 0x01) << 31;\n    if (b < 0x80) return h * 0x100000000 + (l >>> 0);\n\n    throw new Error(\"Expected varint not more than 10 bytes\");\n}\n\nexport function decodeFastPfor(\n    encodedBytes: Uint8Array,\n    expectedValueCount: number,\n    encodedByteLength: number,\n    offset: IntWrapper,\n): Uint32Array {\n    const workspace = createFastPforWireDecodeWorkspace(encodedByteLength >>> 2);\n    return decodeFastPforWithWorkspace(encodedBytes, expectedValueCount, encodedByteLength, offset, workspace);\n}\n\nexport function decodeFastPforWithWorkspace(\n    encodedBytes: Uint8Array,\n    expectedValueCount: number,\n    encodedByteLength: number,\n    offset: IntWrapper,\n    workspace: FastPforWireDecodeWorkspace,\n): Uint32Array {\n    const inputByteOffset = offset.get();\n    if ((encodedByteLength & 3) !== 0) {\n        throw new Error(\n            `FastPFOR: invalid encodedByteLength=${encodedByteLength} at offset=${inputByteOffset} (encodedBytes.length=${encodedBytes.length}; expected a multiple of 4 bytes for an int32 big-endian word stream)`,\n        );\n    }\n\n    const encodedWordCount = encodedByteLength >>> 2;\n    const encodedWordBuffer = ensureFastPforWireEncodedWordsCapacity(workspace, encodedWordCount);\n    decodeBigEndianInt32sInto(encodedBytes, inputByteOffset, encodedByteLength, encodedWordBuffer);\n\n    const decodedValues = decodeFastPforInt32(\n        encodedWordBuffer.subarray(0, encodedWordCount),\n        expectedValueCount,\n        workspace.decoderWorkspace,\n    );\n    offset.add(encodedByteLength);\n    return decodedValues;\n}\n\nexport function decodeZigZagInt32Value(encoded: number): number {\n    return (encoded >>> 1) ^ -(encoded & 1);\n}\n\nexport function decodeZigZagInt64Value(encoded: bigint): bigint {\n    return (encoded >> 1n) ^ -(encoded & 1n);\n}\n\nexport function decodeZigZagFloat64Value(encoded: number): number {\n    return encoded % 2 === 1 ? (encoded + 1) / -2 : encoded / 2;\n}\n\nexport function decodeZigZagInt32(encodedData: Uint32Array): Int32Array {\n    const decodedValues = new Int32Array(encodedData.length);\n    for (let i = 0; i < encodedData.length; i++) {\n        decodedValues[i] = decodeZigZagInt32Value(encodedData[i]);\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagInt64(encodedData: BigUint64Array): BigInt64Array {\n    const decodedValues = new BigInt64Array(encodedData.length);\n    for (let i = 0; i < encodedData.length; i++) {\n        decodedValues[i] = decodeZigZagInt64Value(encodedData[i]);\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagFloat64(encodedData: Float64Array): void {\n    for (let i = 0; i < encodedData.length; i++) {\n        encodedData[i] = decodeZigZagFloat64Value(encodedData[i]);\n    }\n}\n\nexport function decodeUnsignedRleInt32(\n    encodedData: Uint32Array,\n    numRuns: number,\n    numTotalValues?: number,\n): Uint32Array {\n    // If numTotalValues not provided, calculate from runs (nullable case)\n    if (numTotalValues === undefined) {\n        numTotalValues = 0;\n        for (let i = 0; i < numRuns; i++) {\n            numTotalValues += encodedData[i];\n        }\n    }\n\n    const decodedValues = new Uint32Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = encodedData[i];\n        const value = encodedData[i + numRuns];\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\nexport function decodeUnsignedRleInt64(\n    encodedData: BigUint64Array,\n    numRuns: number,\n    numTotalValues?: number,\n): BigUint64Array {\n    // If numTotalValues not provided, calculate from runs (nullable case)\n    if (numTotalValues === undefined) {\n        numTotalValues = 0;\n        for (let i = 0; i < numRuns; i++) {\n            numTotalValues += Number(encodedData[i]);\n        }\n    }\n\n    const decodedValues = new BigUint64Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = Number(encodedData[i]);\n        const value = encodedData[i + numRuns];\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\nexport function decodeUnsignedRleFloat64(\n    encodedData: Float64Array,\n    numRuns: number,\n    numTotalValues: number,\n): Float64Array {\n    const decodedValues = new Float64Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = encodedData[i];\n        const value = encodedData[i + numRuns];\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\n/*\n * In place decoding of the zigzag encoded delta values.\n * Inspired by https://github.com/lemire/JavaFastPFOR/blob/master/src/main/java/me/lemire/integercompression/differential/Delta.java\n */\nexport function decodeZigZagDeltaInt32(data: Uint32Array): Int32Array {\n    const decodedValues = new Int32Array(data.length);\n    decodedValues[0] = decodeZigZagInt32Value(data[0]);\n    const sz0 = (data.length / 4) * 4;\n    let i = 1;\n    if (sz0 >= 4) {\n        for (; i < sz0 - 4; i += 4) {\n            const data1 = data[i];\n            const data2 = data[i + 1];\n            const data3 = data[i + 2];\n            const data4 = data[i + 3];\n\n            decodedValues[i] = decodeZigZagInt32Value(data1) + decodedValues[i - 1];\n            decodedValues[i + 1] = decodeZigZagInt32Value(data2) + decodedValues[i];\n            decodedValues[i + 2] = decodeZigZagInt32Value(data3) + decodedValues[i + 1];\n            decodedValues[i + 3] = decodeZigZagInt32Value(data4) + decodedValues[i + 2];\n        }\n    }\n\n    for (; i !== data.length; ++i) {\n        decodedValues[i] = decodeZigZagInt32Value(data[i]) + decodedValues[i - 1];\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagDeltaInt64(data: BigInt64Array | BigUint64Array): BigInt64Array {\n    const decodedValues = new BigInt64Array(data.length);\n    decodedValues[0] = decodeZigZagInt64Value(data[0]);\n    const sz0 = (data.length / 4) * 4;\n    let i = 1;\n    if (sz0 >= 4) {\n        for (; i < sz0 - 4; i += 4) {\n            const data1 = data[i];\n            const data2 = data[i + 1];\n            const data3 = data[i + 2];\n            const data4 = data[i + 3];\n\n            decodedValues[i] = decodeZigZagInt64Value(data1) + decodedValues[i - 1];\n            decodedValues[i + 1] = decodeZigZagInt64Value(data2) + decodedValues[i];\n            decodedValues[i + 2] = decodeZigZagInt64Value(data3) + decodedValues[i + 1];\n            decodedValues[i + 3] = decodeZigZagInt64Value(data4) + decodedValues[i + 2];\n        }\n    }\n\n    for (; i !== decodedValues.length; ++i) {\n        decodedValues[i] = decodeZigZagInt64Value(data[i]) + decodedValues[i - 1];\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagDeltaFloat64(data: Float64Array) {\n    data[0] = decodeZigZagFloat64Value(data[0]);\n    const sz0 = (data.length / 4) * 4;\n    let i = 1;\n    if (sz0 >= 4) {\n        for (; i < sz0 - 4; i += 4) {\n            const data1 = data[i];\n            const data2 = data[i + 1];\n            const data3 = data[i + 2];\n            const data4 = data[i + 3];\n\n            data[i] = decodeZigZagFloat64Value(data1) + data[i - 1];\n            data[i + 1] = decodeZigZagFloat64Value(data2) + data[i];\n            data[i + 2] = decodeZigZagFloat64Value(data3) + data[i + 1];\n            data[i + 3] = decodeZigZagFloat64Value(data4) + data[i + 2];\n        }\n    }\n\n    for (; i !== data.length; ++i) {\n        data[i] = decodeZigZagFloat64Value(data[i]) + data[i - 1];\n    }\n}\n\nexport function decodeZigZagRleInt32(data: Uint32Array, numRuns: number, numTotalValues?: number): Int32Array {\n    // If numTotalValues not provided, calculate from runs (nullable case)\n    if (numTotalValues === undefined) {\n        numTotalValues = 0;\n        for (let i = 0; i < numRuns; i++) {\n            numTotalValues += data[i];\n        }\n    }\n\n    const decodedValues = new Int32Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = data[i];\n        let value = data[i + numRuns];\n        value = decodeZigZagInt32Value(value);\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagRleInt64(data: BigUint64Array, numRuns: number, numTotalValues?: number): BigInt64Array {\n    // If numTotalValues not provided, calculate from runs (nullable case)\n    if (numTotalValues === undefined) {\n        numTotalValues = 0;\n        for (let i = 0; i < numRuns; i++) {\n            numTotalValues += Number(data[i]);\n        }\n    }\n\n    const decodedValues = new BigInt64Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = Number(data[i]);\n        let value = data[i + numRuns];\n        value = decodeZigZagInt64Value(value);\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\nexport function decodeZigZagRleFloat64(data: Float64Array, numRuns: number, numTotalValues: number): Float64Array {\n    const decodedValues = new Float64Array(numTotalValues);\n    let offset = 0;\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = data[i];\n        let value = data[i + numRuns];\n        value = decodeZigZagFloat64Value(value);\n        decodedValues.fill(value, offset, offset + runLength);\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\n/*\n * Inspired by https://github.com/lemire/JavaFastPFOR/blob/master/src/main/java/me/lemire/integercompression/differential/Delta.java\n */\nexport function fastInverseDelta(data: Uint32Array | Int32Array) {\n    const sz0 = (data.length / 4) * 4;\n    let i = 1;\n    if (sz0 >= 4) {\n        for (let a = data[0]; i < sz0 - 4; i += 4) {\n            a = data[i] += a;\n            a = data[i + 1] += a;\n            a = data[i + 2] += a;\n            a = data[i + 3] += a;\n        }\n    }\n\n    while (i !== data.length) {\n        data[i] += data[i - 1];\n        ++i;\n    }\n}\n\nexport function inverseDelta(data: Uint32Array) {\n    let prevValue = 0;\n    for (let i = 0; i < data.length; i++) {\n        data[i] += prevValue;\n        prevValue = data[i];\n    }\n}\n\n/*\n * In place decoding of the zigzag delta encoded Vec2.\n * Inspired by https://github.com/lemire/JavaFastPFOR/blob/master/src/main/java/me/lemire/integercompression/differential/Delta.java\n */\nexport function decodeComponentwiseDeltaVec2(data: Uint32Array): Int32Array {\n    if (data.length < 2) return new Int32Array(data);\n    const decodedData = new Int32Array(data.length);\n    decodedData[0] = decodeZigZagInt32Value(data[0]);\n    decodedData[1] = decodeZigZagInt32Value(data[1]);\n    const sz0 = (data.length / 4) * 4;\n    let i = 2;\n    if (sz0 >= 4) {\n        for (; i < sz0 - 4; i += 4) {\n            const x1 = data[i];\n            const y1 = data[i + 1];\n            const x2 = data[i + 2];\n            const y2 = data[i + 3];\n\n            decodedData[i] = decodeZigZagInt32Value(x1) + decodedData[i - 2];\n            decodedData[i + 1] = decodeZigZagInt32Value(y1) + decodedData[i - 1];\n            decodedData[i + 2] = decodeZigZagInt32Value(x2) + decodedData[i];\n            decodedData[i + 3] = decodeZigZagInt32Value(y2) + decodedData[i + 1];\n        }\n    }\n\n    for (; i !== data.length; i += 2) {\n        decodedData[i] = decodeZigZagInt32Value(data[i]) + decodedData[i - 2];\n        decodedData[i + 1] = decodeZigZagInt32Value(data[i + 1]) + decodedData[i - 1];\n    }\n    return decodedData;\n}\n\nexport function decodeComponentwiseDeltaVec2Scaled(\n    data: Uint32Array,\n    scale: number,\n    min: number,\n    max: number,\n): Int32Array {\n    if (data.length < 2) return new Int32Array(data);\n    const decodedData = new Int32Array(data.length);\n    let previousVertexX = decodeZigZagInt32Value(data[0]);\n    let previousVertexY = decodeZigZagInt32Value(data[1]);\n    decodedData[0] = clamp(Math.round(previousVertexX * scale), min, max);\n    decodedData[1] = clamp(Math.round(previousVertexY * scale), min, max);\n    const sz0 = data.length / 16;\n    let i = 2;\n    if (sz0 >= 4) {\n        for (; i < sz0 - 4; i += 4) {\n            const x1 = data[i];\n            const y1 = data[i + 1];\n            const currentVertexX = decodeZigZagInt32Value(x1) + previousVertexX;\n            const currentVertexY = decodeZigZagInt32Value(y1) + previousVertexY;\n            decodedData[i] = clamp(Math.round(currentVertexX * scale), min, max);\n            decodedData[i + 1] = clamp(Math.round(currentVertexY * scale), min, max);\n\n            const x2 = data[i + 2];\n            const y2 = data[i + 3];\n            previousVertexX = decodeZigZagInt32Value(x2) + currentVertexX;\n            previousVertexY = decodeZigZagInt32Value(y2) + currentVertexY;\n            decodedData[i + 2] = clamp(Math.round(previousVertexX * scale), min, max);\n            decodedData[i + 3] = clamp(Math.round(previousVertexY * scale), min, max);\n        }\n    }\n\n    for (; i !== data.length; i += 2) {\n        previousVertexX += decodeZigZagInt32Value(data[i]);\n        previousVertexY += decodeZigZagInt32Value(data[i + 1]);\n        decodedData[i] = clamp(Math.round(previousVertexX * scale), min, max);\n        decodedData[i + 1] = clamp(Math.round(previousVertexY * scale), min, max);\n    }\n    return decodedData;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n    return Math.min(max, Math.max(min, n));\n}\n\n/* Transform data to allow util access ------------------------------------------------------------------------ */\n\nexport function decodeZigZagDeltaOfDeltaInt32(data: Uint32Array): Uint32Array {\n    const decodedData = new Int32Array(data.length + 1);\n    decodedData[0] = 0;\n    decodedData[1] = decodeZigZagInt32Value(data[0]);\n    let deltaSum = decodedData[1];\n    for (let i = 2; i !== decodedData.length; ++i) {\n        const zigZagValue = data[i - 1];\n        const delta = decodeZigZagInt32Value(zigZagValue);\n        deltaSum += delta;\n        decodedData[i] = decodedData[i - 1] + deltaSum;\n    }\n\n    return new Uint32Array(decodedData);\n}\n\nexport function decodeZigZagRleDeltaInt32(data: Uint32Array, numRuns: number, numTotalValues: number): Int32Array {\n    const decodedValues = new Int32Array(numTotalValues + 1);\n    decodedValues[0] = 0;\n    let offset = 1;\n    let previousValue = decodedValues[0];\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = data[i];\n        let value = data[i + numRuns];\n        value = decodeZigZagInt32Value(value);\n        for (let j = offset; j < offset + runLength; j++) {\n            decodedValues[j] = value + previousValue;\n            previousValue = decodedValues[j];\n        }\n\n        offset += runLength;\n    }\n    return decodedValues;\n}\n\nexport function decodeRleDeltaInt32(data: Uint32Array, numRuns: number, numTotalValues: number): Uint32Array {\n    const decodedValues = new Uint32Array(numTotalValues + 1);\n    decodedValues[0] = 0;\n    let offset = 1;\n    let previousValue = decodedValues[0];\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = data[i];\n        const value = data[i + numRuns];\n        for (let j = offset; j < offset + runLength; j++) {\n            decodedValues[j] = value + previousValue;\n            previousValue = decodedValues[j];\n        }\n\n        offset += runLength;\n    }\n\n    return decodedValues;\n}\n\n/**\n * Decode Delta-RLE with multiple runs by fully reconstructing values.\n *\n * @param data RLE encoded data: [run1, run2, ..., value1, value2, ...]\n * @param numRuns Number of runs in the RLE encoding\n * @param numValues Total number of values to reconstruct\n * @returns Reconstructed values with deltas applied\n */\nexport function decodeDeltaRleInt32(data: Uint32Array, numRuns: number, numValues: number): Int32Array {\n    const result = new Int32Array(numValues);\n    let outPos = 0;\n    let previousValue = 0;\n\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = data[i];\n        const zigZagDelta = data[i + numRuns];\n        const delta = decodeZigZagInt32Value(zigZagDelta);\n\n        for (let j = 0; j < runLength; j++) {\n            previousValue += delta;\n            result[outPos++] = previousValue;\n        }\n    }\n\n    return result;\n}\n\n/**\n * Decode Delta-RLE with multiple runs for 64-bit integers.\n */\nexport function decodeDeltaRleInt64(data: BigUint64Array, numRuns: number, numValues: number): BigInt64Array {\n    const result = new BigInt64Array(numValues);\n    let outPos = 0;\n    let previousValue = 0n;\n\n    for (let i = 0; i < numRuns; i++) {\n        const runLength = Number(data[i]);\n        const zigZagDelta = data[i + numRuns];\n        const delta = decodeZigZagInt64Value(zigZagDelta);\n\n        for (let j = 0; j < runLength; j++) {\n            previousValue += delta;\n            result[outPos++] = previousValue;\n        }\n    }\n\n    return result;\n}\n\nexport function decodeUnsignedZigZagDeltaInt32(data: Uint32Array): Uint32Array {\n    const decodedValues = new Uint32Array(data.length);\n    decodedValues[0] = decodeZigZagInt32Value(data[0]) >>> 0;\n    for (let i = 1; i < data.length; i++) {\n        decodedValues[i] = (decodedValues[i - 1] + decodeZigZagInt32Value(data[i])) >>> 0;\n    }\n    return decodedValues;\n}\n\nexport function decodeUnsignedZigZagDeltaInt64(data: BigUint64Array): BigUint64Array {\n    const decodedValues = new BigUint64Array(data.length);\n    decodedValues[0] = BigInt.asUintN(64, decodeZigZagInt64Value(data[0]));\n    for (let i = 1; i < data.length; i++) {\n        decodedValues[i] = BigInt.asUintN(64, decodedValues[i - 1] + decodeZigZagInt64Value(data[i]));\n    }\n    return decodedValues;\n}\n\nexport function decodeUnsignedComponentwiseDeltaVec2(data: Uint32Array): Uint32Array {\n    if (data.length < 2) {\n        return new Uint32Array(data);\n    }\n\n    const decodedData = new Uint32Array(data.length);\n    decodedData[0] = decodeZigZagInt32Value(data[0]) >>> 0;\n    decodedData[1] = decodeZigZagInt32Value(data[1]) >>> 0;\n    for (let i = 2; i < data.length; i += 2) {\n        decodedData[i] = (decodedData[i - 2] + decodeZigZagInt32Value(data[i])) >>> 0;\n        decodedData[i + 1] = (decodedData[i - 1] + decodeZigZagInt32Value(data[i + 1])) >>> 0;\n    }\n    return decodedData;\n}\n\nexport function decodeUnsignedComponentwiseDeltaVec2Scaled(\n    data: Uint32Array,\n    scale: number,\n    min: number,\n    max: number,\n): Uint32Array {\n    const scaledValues = decodeComponentwiseDeltaVec2Scaled(data, scale, min, max);\n    return new Uint32Array(scaledValues);\n}\n\nexport function decodeUnsignedConstRleInt32(data: Int32Array | Uint32Array): number {\n    return data[1];\n}\n\nexport function decodeZigZagConstRleInt32(data: Int32Array | Uint32Array): number {\n    return decodeZigZagInt32Value(data[1]);\n}\n\nexport function decodeZigZagSequenceRleInt32(data: Int32Array | Uint32Array): [baseValue: number, delta: number] {\n    /* base value and delta value are equal */\n    if (data.length === 2) {\n        const value = decodeZigZagInt32Value(data[1]);\n        return [value, value];\n    }\n\n    /* base value and delta value are not equal -> 2 runs and 2 values*/\n    const base = decodeZigZagInt32Value(data[2]);\n    const delta = decodeZigZagInt32Value(data[3]);\n    return [base, delta];\n}\n\nexport function decodeUnsignedConstRleInt64(data: BigInt64Array | BigUint64Array): bigint {\n    return data[1];\n}\n\nexport function decodeZigZagConstRleInt64(data: BigInt64Array | BigUint64Array): bigint {\n    return decodeZigZagInt64Value(data[1]);\n}\n\nexport function decodeZigZagSequenceRleInt64(data: BigInt64Array | BigUint64Array): [baseValue: bigint, delta: bigint] {\n    /* base value and delta value are equal */\n    if (data.length === 2) {\n        const value = decodeZigZagInt64Value(data[1]);\n        return [value, value];\n    }\n\n    /* base value and delta value are not equal -> 2 runs and 2 values*/\n    const base = decodeZigZagInt64Value(data[2]);\n    const delta = decodeZigZagInt64Value(data[3]);\n    return [base, delta];\n}\n"
  },
  {
    "path": "ts/src/decoding/integerStreamDecoder.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport {\n    decodeSignedInt32Stream,\n    decodeSignedInt64AsFloat64Stream,\n    decodeSignedInt64Stream,\n    decodeSignedConstInt32Stream,\n    decodeSignedConstInt64Stream,\n    decodeUnsignedInt32Stream,\n    decodeUnsignedConstInt32Stream,\n    decodeUnsignedConstInt64Stream,\n    decodeUnsignedInt64AsFloat64Stream,\n    decodeUnsignedInt64Stream,\n    getVectorType,\n} from \"./integerStreamDecoder\";\nimport { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport { VectorType } from \"../vector/vectorType\";\nimport IntWrapper from \"./intWrapper\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport { createRleMetadata, createStreamMetadata } from \"./decodingTestUtils\";\nimport {\n    encodeFloat64,\n    encodeSignedInt32Stream,\n    encodeInt64SignedDelta,\n    encodeInt64SignedDeltaRle,\n    encodeInt64SignedNone,\n    encodeInt64SignedRle,\n    encodeInt64UnsignedNone,\n    encodeUnsignedInt32Stream,\n} from \"../encoding/integerStreamEncoder\";\nimport {\n    encodeDeltaRleInt32,\n    encodeVarintFloat64,\n    encodeVarintInt64,\n    encodeZigZagInt32Value,\n    encodeZigZagInt64Value,\n} from \"../encoding/integerEncodingUtils\";\n\ndescribe(\"getVectorType\", () => {\n    it(\"should return FLAT for RLE with 0 runs\", () => {\n        const metadata = createRleMetadata(LogicalLevelTechnique.RLE, LogicalLevelTechnique.RLE, 0, 0);\n        const result = getVectorType(metadata, 0, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.FLAT);\n    });\n\n    it(\"should return CONST for single run RLE\", () => {\n        const metadata = createRleMetadata(LogicalLevelTechnique.RLE, LogicalLevelTechnique.RLE, 1, 0);\n        const result = getVectorType(metadata, 0, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.CONST);\n    });\n\n    it(\"should return FLAT for NONE with 0 runs\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 0);\n        const result = getVectorType(metadata, 0, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.FLAT);\n    });\n\n    it(\"should return CONST for NONE with single run\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n        const result = getVectorType(metadata, 0, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.CONST);\n    });\n\n    it(\"should return FLAT for features and values mismatch\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, 1);\n        const result = getVectorType(metadata, 2, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.FLAT);\n    });\n\n    it(\"should return SEQUENCE for single RLE run\", () => {\n        const metadata = createRleMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, 1, 1);\n        const result = getVectorType(metadata, 1, new Uint8Array(), new IntWrapper(0));\n        expect(result).toBe(VectorType.SEQUENCE);\n    });\n\n    it(\"should return SEQUENCE for RLE run with 2 runs\", () => {\n        const metadata = createRleMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, 2, 5);\n        const twoRunUnitDeltaVarintPayload = new Uint8Array([1, 4, 2, 2]); // Can't achieve this array using the encoding method...\n        const result = getVectorType(metadata, 5, twoRunUnitDeltaVarintPayload, new IntWrapper(0));\n        expect(result).toBe(VectorType.SEQUENCE);\n    });\n\n    it(\"should probe 64-bit varints without throwing for large DELTA+RLE base values\", () => {\n        const metadata = createRleMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, 2, 4);\n        const data = encodeInt64SignedDeltaRle([\n            [1, 9_234_567_890n],\n            [3, 0n],\n        ]);\n\n        const result = getVectorType(metadata, 4, data, new IntWrapper(0), \"int64\");\n\n        expect(result).toBe(VectorType.FLAT);\n    });\n\n    it(\"should detect SEQUENCE for DELTA+RLE direct int32 payloads with unit deltas\", () => {\n        const unitDeltaEncodedValue = encodeZigZagInt32Value(1);\n        const twoRunUnitDeltaWords = new Uint32Array([1, 4, unitDeltaEncodedValue, unitDeltaEncodedValue]);\n        const twoRunUnitDeltaPayload = new Uint8Array(twoRunUnitDeltaWords.buffer.slice(0));\n        const metadata = {\n            ...createRleMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, 2, 5),\n            physicalLevelTechnique: PhysicalLevelTechnique.NONE,\n            byteLength: twoRunUnitDeltaPayload.byteLength,\n        };\n\n        const result = getVectorType(metadata, 5, twoRunUnitDeltaPayload, new IntWrapper(0));\n\n        expect(result).toBe(VectorType.SEQUENCE);\n    });\n\n    it(\"should return FLAT for DELTA+RLE direct int32 payloads with non-unit deltas\", () => {\n        const increasingOddValues = new Int32Array([1, 3, 5, 7, 9]);\n        const { data: encodedWords, runs: deltaRleRunCount } = encodeDeltaRleInt32(increasingOddValues);\n        const twoRunMixedDeltaPayload = new Uint8Array(encodedWords.buffer.slice(0));\n        const metadata = {\n            ...createRleMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.RLE, deltaRleRunCount, 5),\n            physicalLevelTechnique: PhysicalLevelTechnique.NONE,\n            byteLength: twoRunMixedDeltaPayload.byteLength,\n        };\n\n        const result = getVectorType(metadata, 5, twoRunMixedDeltaPayload, new IntWrapper(0));\n\n        expect(result).toBe(VectorType.FLAT);\n    });\n});\n\ndescribe(\"decodeUnsignedInt32Stream\", () => {\n    it(\"should decode with PhysicalLevelTechnique.NONE\", () => {\n        const expectedValues = new Uint32Array([10, 20, 30]);\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata);\n        const result = decodeUnsignedInt32Stream(data, new IntWrapper(0), metadata);\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode MORTON\", () => {\n        const expectedValues = new Uint32Array([10, 15, 18, 20]);\n        const metadata = createStreamMetadata(LogicalLevelTechnique.MORTON, LogicalLevelTechnique.NONE, 4);\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata);\n        const result = decodeUnsignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode nullable MORTON fully populated\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.MORTON, LogicalLevelTechnique.NONE, 4);\n        const expectedValues = new Uint32Array([10, 15, 18, 20]);\n        const bitVector = new BitVector(new Uint8Array([0b1111]), 4);\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata, bitVector);\n        const offset = new IntWrapper(0);\n\n        const result = decodeUnsignedInt32Stream(data, offset, metadata, undefined, bitVector);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode nullable MORTON null values\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.MORTON, LogicalLevelTechnique.NONE, 3);\n        const expectedValues = new Uint32Array([10, 0, 15, 0, 18]);\n        const bitVector = new BitVector(new Uint8Array([0b10101]), 5);\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata, bitVector);\n\n        const result = decodeUnsignedInt32Stream(data, new IntWrapper(0), metadata, undefined, bitVector);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode DELTA with RLE\", () => {\n        const expectedValues = new Uint32Array([10, 12, 14, 15, 16]);\n        const metadata = createRleMetadata(\n            LogicalLevelTechnique.DELTA,\n            LogicalLevelTechnique.RLE,\n            3,\n            expectedValues.length,\n        );\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata);\n\n        const result = decodeUnsignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n});\n\ndescribe(\"decodeSignedInt32Stream\", () => {\n    it(\"should decode NONE signed with Int32\", () => {\n        const expectedValues = new Int32Array([2, -4, 6, -8]);\n        const metadata = createStreamMetadata(\n            LogicalLevelTechnique.NONE,\n            LogicalLevelTechnique.NONE,\n            expectedValues.length,\n        );\n        const data = encodeSignedInt32Stream(expectedValues, metadata);\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode nullable NONE signed Int32 partially populated\", () => {\n        const expectedValues = new Int32Array([0, 15, 0, 20]);\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 2);\n        const bitVector = new BitVector(new Uint8Array([0b1010]), 4);\n        const data = encodeSignedInt32Stream(expectedValues, metadata, bitVector);\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata, undefined, bitVector);\n\n        expect(result).toEqual(new Int32Array([0, 15, 0, 20]));\n    });\n\n    it(\"should decode DELTA signed with Int32\", () => {\n        const expectedValues = new Int32Array([10, 12, 14, 16]);\n        const metadata = createStreamMetadata(\n            LogicalLevelTechnique.DELTA,\n            LogicalLevelTechnique.NONE,\n            expectedValues.length,\n        );\n        const data = encodeSignedInt32Stream(expectedValues, metadata);\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode nullable DELTA signed Int32 with null values\", () => {\n        const logicalValueCount = 5;\n        const physicalValueCount = 3;\n        const metadata = createStreamMetadata(\n            LogicalLevelTechnique.DELTA,\n            LogicalLevelTechnique.NONE,\n            physicalValueCount,\n        );\n        const expectedValues = new Int32Array([0, 2, 0, 4, 6]);\n        const bitVector = new BitVector(new Uint8Array([0b00011010]), logicalValueCount);\n        const data = encodeSignedInt32Stream(expectedValues, metadata, bitVector);\n\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata, undefined, bitVector);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode Componentwise Delta with Int32\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.COMPONENTWISE_DELTA, LogicalLevelTechnique.NONE, 4);\n        const expectedValues = new Int32Array([10, 20, 11, 21]);\n        const data = encodeSignedInt32Stream(expectedValues, metadata);\n\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode Componentwise Delta Scaled with Int32\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.COMPONENTWISE_DELTA, LogicalLevelTechnique.NONE, 4);\n        const expectedValues = new Int32Array([100, 200, 110, 220]);\n        const scalingData = { extent: 4096, min: 0, max: 4096, scale: 2.0 };\n        const data = encodeSignedInt32Stream(expectedValues, metadata, undefined, scalingData);\n\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata, scalingData);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode RLE signed with Int32\", () => {\n        const expectedValues = new Int32Array([100, 100, 100, -50, -50]);\n        const runs = 2;\n        const metadata = createRleMetadata(\n            LogicalLevelTechnique.RLE,\n            LogicalLevelTechnique.NONE,\n            runs,\n            expectedValues.length,\n        );\n        const data = encodeSignedInt32Stream(expectedValues, metadata);\n        const result = decodeSignedInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode nullable RLE Int32 partially populated\", () => {\n        let metadata = createStreamMetadata(LogicalLevelTechnique.RLE, LogicalLevelTechnique.NONE, 2);\n        const expectedValues = new Uint32Array([0, 15, 0, 20]);\n        const bitVector = new BitVector(new Uint8Array([0b1010]), 4);\n        const data = encodeUnsignedInt32Stream(expectedValues, metadata, bitVector);\n        metadata = createRleMetadata(LogicalLevelTechnique.RLE, LogicalLevelTechnique.NONE, 2, 2);\n        const result = decodeUnsignedInt32Stream(data, new IntWrapper(0), metadata, undefined, bitVector);\n\n        expect(result).toEqual(new Uint32Array([0, 15, 0, 20]));\n    });\n});\n\ndescribe(\"decodeSignedConstInt32Stream\", () => {\n    it(\"should decode signed const Int32\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n        const data = encodeSignedInt32Stream(new Int32Array([-8]), metadata);\n\n        const result = decodeSignedConstInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toBe(-8);\n    });\n});\n\ndescribe(\"decodeUnsignedConstInt32Stream\", () => {\n    it(\"should decode unsigned const Int32\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n        const data = encodeUnsignedInt32Stream(new Uint32Array([0xffffffff]), metadata);\n\n        const result = decodeUnsignedConstInt32Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toBe(0xffffffff);\n    });\n\n    it(\"should throw for unsupported technique\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.PDE, LogicalLevelTechnique.NONE, 3);\n        const offset = new IntWrapper(0);\n        const bitVector = new BitVector(new Uint8Array([0b00000111]), 3);\n\n        expect(() => decodeUnsignedInt32Stream(new Uint8Array([]), offset, metadata, undefined, bitVector)).toThrow(\n            \"The specified Logical level technique is not supported\",\n        );\n    });\n});\n\ndescribe(\"decodeInt64AsFloat64Stream\", () => {\n    it(\"should decode NONE unsigned\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n        const expectedValues = new Float64Array([1, 2, 3]);\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, false);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeUnsignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode NONE signed\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n        const expectedValues = new Float64Array([2, 5, 3]);\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, true);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeSignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode RLE unsigned\", () => {\n        const expectedValues = new Float64Array([10, 10, 10, 20, 20]);\n        const runs = 2;\n        const metadata = createRleMetadata(\n            LogicalLevelTechnique.RLE,\n            LogicalLevelTechnique.NONE,\n            runs,\n            expectedValues.length,\n        );\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, false);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeUnsignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode RLE signed\", () => {\n        const expectedValues = new Float64Array([10, 10, 10, 20, 20]);\n        const runs = 2;\n        const metadata = createRleMetadata(\n            LogicalLevelTechnique.RLE,\n            LogicalLevelTechnique.NONE,\n            runs,\n            expectedValues.length,\n        );\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, true);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeSignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode DELTA without RLE\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.DELTA);\n        const expectedValues = new Float64Array([2, 4, 6]);\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, true);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeSignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(expectedValues);\n    });\n\n    it(\"should decode DELTA with RLE\", () => {\n        const expectedValues = new Float64Array([10, 12, 14, 16, 18]);\n        const runs = 2;\n        const metadata = createRleMetadata(\n            LogicalLevelTechnique.DELTA,\n            LogicalLevelTechnique.RLE,\n            runs,\n            expectedValues.length,\n        );\n        const encodedValues = encodeFloat64(new Float64Array(expectedValues), metadata, true);\n        const data = encodeVarintFloat64(encodedValues);\n        const result = decodeSignedInt64AsFloat64Stream(data, new IntWrapper(0), metadata);\n\n        expect(result).toEqual(new Float64Array([10, 12, 14, 16, 18]));\n    });\n\n    it(\"should throw for unsupported technique\", () => {\n        const metadata = createStreamMetadata(LogicalLevelTechnique.MORTON);\n        const values = new Uint8Array(new Float64Array([1, 2, 3]).buffer);\n\n        expect(() => decodeSignedInt64AsFloat64Stream(values, new IntWrapper(0), metadata)).toThrow(\n            \"The specified Logical level technique is not supported: MORTON\",\n        );\n    });\n});\n\ndescribe(\"decodeInt64Stream\", () => {\n    describe(\"unsigned DELTA with RLE\", () => {\n        it(\"should decode unsigned DELTA with RLE\", () => {\n            const expectedValues = new BigUint64Array([10n, 12n, 14n, 15n, 16n]);\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.DELTA,\n                LogicalLevelTechnique.RLE,\n                3,\n                expectedValues.length,\n            );\n            const encodedValues = new BigUint64Array([\n                1n,\n                2n,\n                2n,\n                encodeZigZagInt64Value(10n),\n                encodeZigZagInt64Value(2n),\n                encodeZigZagInt64Value(1n),\n            ]);\n            const data = encodeVarintInt64(encodedValues);\n            const offset = new IntWrapper(0);\n\n            const result = decodeUnsignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n    });\n\n    describe(\"DELTA with RLE\", () => {\n        it(\"should decode DELTA with RLE\", () => {\n            const numRleValues = 5;\n            const runs = 3;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.DELTA,\n                LogicalLevelTechnique.RLE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([10n, 12n, 14n, 15n, 16n]);\n            const data = encodeInt64SignedDeltaRle([\n                [1, 10n],\n                [2, 2n],\n                [2, 1n],\n            ]);\n            const offset = new IntWrapper(0);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode DELTA with RLE with all non-null values\", () => {\n            const numRleValues = 5;\n            const runs = 3;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.DELTA,\n                LogicalLevelTechnique.RLE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([10n, 12n, 14n, 15n, 16n]);\n            const data = encodeInt64SignedDeltaRle([\n                [1, 10n],\n                [2, 2n],\n                [2, 1n],\n            ]);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00011111]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode DELTA with RLE with null values\", () => {\n            const numRleValues = 3;\n            const runs = 2;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.DELTA,\n                LogicalLevelTechnique.RLE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([10n, 0n, 12n, 0n, 14n]);\n            const data = encodeInt64SignedDeltaRle([\n                [1, 10n],\n                [2, 2n],\n            ]);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00010101]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n    });\n\n    describe(\"DELTA without RLE\", () => {\n        it(\"should decode DELTA without RLE\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.DELTA);\n            const expectedValues = new BigInt64Array([2n, 4n, 6n]);\n            const data = encodeInt64SignedDelta(expectedValues);\n            const offset = new IntWrapper(0);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode DELTA without RLE with all non-null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.DELTA);\n            const expectedValues = new BigInt64Array([2n, 4n, 6n]);\n            const data = encodeInt64SignedDelta(expectedValues);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00000111]), 3);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode DELTA without RLE with null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.NONE, 5);\n            const expectedValues = new BigInt64Array([0n, 2n, 0n, 4n, 6n]);\n            const nonNullValues = new BigInt64Array([2n, 4n, 6n]);\n            const data = encodeInt64SignedDelta(nonNullValues);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00011010]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n    });\n\n    describe(\"RLE\", () => {\n        it(\"should decode RLE\", () => {\n            const numRleValues = 5;\n            const runs = 2;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.RLE,\n                LogicalLevelTechnique.NONE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([100n, 100n, 100n, -50n, -50n]);\n            const data = encodeInt64SignedRle([\n                [3, 100n],\n                [2, -50n],\n            ]);\n            const offset = new IntWrapper(0);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode RLE with all non-null values\", () => {\n            const numRleValues = 5;\n            const runs = 2;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.RLE,\n                LogicalLevelTechnique.NONE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([100n, 100n, 100n, -50n, -50n]);\n            const data = encodeInt64SignedRle([\n                [3, 100n],\n                [2, -50n],\n            ]);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00011111]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode RLE with null values\", () => {\n            const numRleValues = 3;\n            const runs = 2;\n            const metadata = createRleMetadata(\n                LogicalLevelTechnique.RLE,\n                LogicalLevelTechnique.NONE,\n                runs,\n                numRleValues,\n            );\n            const expectedValues = new BigInt64Array([100n, 0n, 100n, 0n, -50n]);\n            const data = encodeInt64SignedRle([\n                [2, 100n],\n                [1, -50n],\n            ]);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00010101]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n    });\n\n    describe(\"NONE\", () => {\n        it(\"should decode NONE signed\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n            const expectedValues = new BigInt64Array([2n, -4n, 6n]);\n            const data = encodeInt64SignedNone(expectedValues);\n            const offset = new IntWrapper(0);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode NONE signed min int64\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n            const expectedValues = new BigInt64Array([-(2n ** 63n)]);\n            const data = encodeInt64SignedNone(expectedValues);\n            const offset = new IntWrapper(0);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode NONE unsigned\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n            const expectedValues = new BigUint64Array([1n, 2n, 3n]);\n            const data = encodeInt64UnsignedNone(new BigInt64Array(expectedValues));\n            const offset = new IntWrapper(0);\n\n            const result = decodeUnsignedInt64Stream(data, offset, metadata);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode signed const Int64\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n            const data = encodeInt64SignedNone(new BigInt64Array([-8n]));\n\n            const result = decodeSignedConstInt64Stream(data, new IntWrapper(0), metadata);\n\n            expect(result).toBe(-8n);\n        });\n\n        it(\"should decode unsigned const Int64\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 1);\n            const data = encodeInt64UnsignedNone(new BigInt64Array([0xffffffffffffffffn]));\n\n            const result = decodeUnsignedConstInt64Stream(data, new IntWrapper(0), metadata);\n\n            expect(result).toBe(0xffffffffffffffffn);\n        });\n\n        it(\"should decode NONE signed with all non-null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n            const expectedValues = new BigInt64Array([2n, -4n, 6n]);\n            const data = encodeInt64SignedNone(expectedValues);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00000111]), 3);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode NONE signed with null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 5);\n            const expectedValues = new BigInt64Array([2n, 0n, -4n, 0n, 6n]);\n            const nonNullValues = new BigInt64Array([2n, -4n, 6n]);\n            const data = encodeInt64SignedNone(nonNullValues);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00010101]), 5);\n\n            const result = decodeSignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode NONE unsigned with all non-null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE);\n            const expectedValues = new BigUint64Array([1n, 2n, 3n]);\n            const data = encodeInt64UnsignedNone(new BigInt64Array(expectedValues));\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00000111]), 3);\n\n            const result = decodeUnsignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n\n        it(\"should decode NONE unsigned with null values\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, 5);\n            const expectedValues = new BigUint64Array([0n, 1n, 2n, 0n, 3n]);\n            const nonNullValues = new BigInt64Array([1n, 2n, 3n]);\n            const data = encodeInt64UnsignedNone(nonNullValues);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00010110]), 5);\n\n            const result = decodeUnsignedInt64Stream(data, offset, metadata, bitVector);\n\n            expect(result).toEqual(expectedValues);\n        });\n    });\n\n    describe(\"error handling\", () => {\n        it(\"should throw for unsupported technique\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.MORTON);\n            const data = encodeInt64UnsignedNone(new BigInt64Array([1n, 2n, 3n]));\n            const offset = new IntWrapper(0);\n            expect(() => decodeSignedInt64Stream(data, offset, metadata)).toThrow(\n                \"The specified Logical level technique is not supported: MORTON\",\n            );\n        });\n\n        it(\"should throw for unsupported technique with nullable\", () => {\n            const metadata = createStreamMetadata(LogicalLevelTechnique.COMPONENTWISE_DELTA);\n            const values = new BigInt64Array([1n, 2n, 3n]);\n            const data = encodeInt64UnsignedNone(values);\n            const offset = new IntWrapper(0);\n            const bitVector = new BitVector(new Uint8Array([0b00000111]), 3);\n            expect(() => decodeSignedInt64Stream(data, offset, metadata, bitVector)).toThrow();\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/integerStreamDecoder.ts",
    "content": "import { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport IntWrapper from \"./intWrapper\";\nimport {\n    decodeComponentwiseDeltaVec2,\n    decodeComponentwiseDeltaVec2Scaled,\n    decodeDeltaRleInt32,\n    decodeDeltaRleInt64,\n    decodeFastPfor,\n    decodeUnsignedComponentwiseDeltaVec2,\n    decodeUnsignedComponentwiseDeltaVec2Scaled,\n    decodeUnsignedConstRleInt32,\n    decodeUnsignedConstRleInt64,\n    decodeUnsignedRleInt32,\n    decodeUnsignedRleInt64,\n    decodeUnsignedRleFloat64,\n    decodeUnsignedZigZagDeltaInt32,\n    decodeUnsignedZigZagDeltaInt64,\n    decodeVarintInt32,\n    decodeVarintInt64,\n    decodeVarintFloat64,\n    decodeZigZagInt32,\n    decodeZigZagInt64,\n    decodeZigZagFloat64,\n    decodeZigZagConstRleInt32,\n    decodeZigZagConstRleInt64,\n    decodeZigZagDeltaInt32,\n    decodeZigZagDeltaInt64,\n    decodeZigZagDeltaFloat64,\n    decodeZigZagSequenceRleInt32,\n    decodeZigZagSequenceRleInt64,\n    decodeZigZagInt32Value,\n    decodeZigZagInt64Value,\n    fastInverseDelta,\n    inverseDelta,\n    decodeRleDeltaInt32,\n    decodeZigZagDeltaOfDeltaInt32,\n    decodeZigZagRleDeltaInt32,\n    decodeZigZagRleInt32,\n    decodeZigZagRleInt64,\n    decodeZigZagRleFloat64,\n} from \"./integerDecodingUtils\";\nimport { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport type { StreamMetadata, RleEncodedStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport { VectorType } from \"../vector/vectorType\";\nimport type GeometryScaling from \"./geometryScaling\";\nimport { unpackNullable } from \"./unpackNullableUtils\";\n\nexport function decodeSignedInt32Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n    scalingData?: GeometryScaling,\n    nullabilityBuffer?: BitVector,\n): Int32Array {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n    return decodeSignedInt32(values, streamMetadata, scalingData, nullabilityBuffer);\n}\n\nexport function decodeUnsignedInt32Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n    scalingData?: GeometryScaling,\n    nullabilityBuffer?: BitVector,\n): Uint32Array {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n    return decodeUnsignedInt32(values, streamMetadata, scalingData, nullabilityBuffer);\n}\n\nexport function decodeLengthStreamToOffsetBuffer(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): Uint32Array {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n    return decodeLengthToOffsetBuffer(values, streamMetadata);\n}\n\nfunction decodePhysicalLevelTechnique(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): Uint32Array {\n    const physicalLevelTechnique = streamMetadata.physicalLevelTechnique;\n    switch (physicalLevelTechnique) {\n        case PhysicalLevelTechnique.FAST_PFOR:\n            return decodeFastPfor(data, streamMetadata.numValues, streamMetadata.byteLength, offset);\n        case PhysicalLevelTechnique.VARINT:\n            return decodeVarintInt32(data, offset, streamMetadata.numValues);\n        case PhysicalLevelTechnique.NONE: {\n            const dataOffset = offset.get();\n            const byteLength = streamMetadata.byteLength;\n            offset.add(byteLength);\n            const slice = data.subarray(dataOffset, offset.get());\n            return new Uint32Array(slice);\n        }\n        default:\n            throw new Error(`Specified physicalLevelTechnique ${physicalLevelTechnique} is not supported (yet).`);\n    }\n}\n\nexport function decodeSignedConstInt32Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): number {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n\n    if (values.length === 1) {\n        return decodeZigZagInt32Value(values[0]);\n    }\n\n    return decodeZigZagConstRleInt32(values);\n}\n\nexport function decodeUnsignedConstInt32Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): number {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n\n    if (values.length === 1) {\n        return values[0];\n    }\n\n    return decodeUnsignedConstRleInt32(values);\n}\n\nexport function decodeSequenceInt32Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): [baseValue: number, delta: number] {\n    const values = decodePhysicalLevelTechnique(data, offset, streamMetadata);\n    return decodeZigZagSequenceRleInt32(values);\n}\n\nexport function decodeSequenceInt64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): [baseValue: bigint, delta: bigint] {\n    const values = decodeVarintInt64(data, offset, streamMetadata.numValues);\n    return decodeZigZagSequenceRleInt64(values);\n}\n\nexport function decodeSignedInt64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n    nullabilityBuffer?: BitVector,\n): BigInt64Array {\n    const values = decodeVarintInt64(data, offset, streamMetadata.numValues);\n    return decodeSignedInt64(values, streamMetadata, nullabilityBuffer);\n}\n\nexport function decodeUnsignedInt64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n    nullabilityBuffer?: BitVector,\n): BigUint64Array {\n    const values = decodeVarintInt64(data, offset, streamMetadata.numValues);\n    return decodeUnsignedInt64(values, streamMetadata, nullabilityBuffer);\n}\n\nexport function decodeSignedInt64AsFloat64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): Float64Array {\n    const values = decodeVarintFloat64(data, offset, streamMetadata.numValues);\n    return decodeFloat64Values(values, streamMetadata, true);\n}\n\nexport function decodeUnsignedInt64AsFloat64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): Float64Array {\n    const values = decodeVarintFloat64(data, offset, streamMetadata.numValues);\n    return decodeFloat64Values(values, streamMetadata, false);\n}\n\nexport function decodeSignedConstInt64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): bigint {\n    const values = decodeVarintInt64(data, offset, streamMetadata.numValues);\n\n    if (values.length === 1) {\n        return decodeZigZagInt64Value(values[0]);\n    }\n\n    return decodeZigZagConstRleInt64(values);\n}\n\nexport function decodeUnsignedConstInt64Stream(\n    data: Uint8Array,\n    offset: IntWrapper,\n    streamMetadata: StreamMetadata,\n): bigint {\n    const values = decodeVarintInt64(data, offset, streamMetadata.numValues);\n\n    if (values.length === 1) {\n        return values[0];\n    }\n\n    return decodeUnsignedConstRleInt64(values);\n}\n\n/**\n * This method decodes integer streams.\n * Currently the encoder uses only fixed combinations of encodings.\n * For performance reasons it is also uses a fixed combination of the encodings on the decoding side.\n * The following encodings and combinations are used:\n *   - Morton Delta -> always sorted so not ZigZag encoding needed\n *   - Delta -> currently always in combination with ZigZag encoding\n *   - Rle -> in combination with ZigZag encoding if data type is signed\n *   - Delta Rle\n *   - Componentwise Delta -> always ZigZag encoding is used\n */\nfunction decodeSignedInt32(\n    values: Uint32Array,\n    streamMetadata: StreamMetadata,\n    scalingData?: GeometryScaling,\n    nullabilityBuffer?: BitVector,\n): Int32Array {\n    let decodedValues: Int32Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n                if (!nullabilityBuffer) {\n                    return decodeDeltaRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues);\n                }\n                values = decodeUnsignedRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues);\n                decodedValues = decodeZigZagDeltaInt32(values);\n            } else {\n                decodedValues = decodeZigZagDeltaInt32(values);\n            }\n            break;\n        case LogicalLevelTechnique.RLE:\n            decodedValues = decodeZigZagRleInt32(\n                values,\n                (streamMetadata as RleEncodedStreamMetadata).runs,\n                (streamMetadata as RleEncodedStreamMetadata).numRleValues,\n            );\n            break;\n        case LogicalLevelTechnique.MORTON:\n            fastInverseDelta(values);\n            decodedValues = new Int32Array(values);\n            break;\n        case LogicalLevelTechnique.COMPONENTWISE_DELTA:\n            if (scalingData && !nullabilityBuffer) {\n                return decodeComponentwiseDeltaVec2Scaled(values, scalingData.scale, scalingData.min, scalingData.max);\n            }\n            decodedValues = decodeComponentwiseDeltaVec2(values);\n            break;\n        case LogicalLevelTechnique.NONE:\n            decodedValues = decodeZigZagInt32(values);\n            break;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n\n    if (nullabilityBuffer) {\n        return unpackNullable(decodedValues, nullabilityBuffer, 0);\n    }\n    return decodedValues;\n}\n\nfunction decodeUnsignedInt32(\n    values: Uint32Array,\n    streamMetadata: StreamMetadata,\n    scalingData?: GeometryScaling,\n    nullabilityBuffer?: BitVector,\n): Uint32Array {\n    let decodedValues: Uint32Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n                const deltaValues = decodeUnsignedRleInt32(values, rleMetadata.runs, rleMetadata.numRleValues);\n                decodedValues = decodeUnsignedZigZagDeltaInt32(deltaValues);\n            } else {\n                decodedValues = decodeUnsignedZigZagDeltaInt32(values);\n            }\n            break;\n        case LogicalLevelTechnique.RLE:\n            decodedValues = decodeUnsignedRleInt32(\n                values,\n                (streamMetadata as RleEncodedStreamMetadata).runs,\n                (streamMetadata as RleEncodedStreamMetadata).numRleValues,\n            );\n            break;\n        case LogicalLevelTechnique.MORTON:\n            fastInverseDelta(values);\n            decodedValues = values;\n            break;\n        case LogicalLevelTechnique.COMPONENTWISE_DELTA:\n            if (scalingData && !nullabilityBuffer) {\n                decodedValues = decodeUnsignedComponentwiseDeltaVec2Scaled(\n                    values,\n                    scalingData.scale,\n                    scalingData.min,\n                    scalingData.max,\n                );\n            } else {\n                decodedValues = decodeUnsignedComponentwiseDeltaVec2(values);\n            }\n            break;\n        case LogicalLevelTechnique.NONE:\n            decodedValues = values;\n            break;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n\n    if (nullabilityBuffer) {\n        return unpackNullable(decodedValues, nullabilityBuffer, 0);\n    }\n    return decodedValues;\n}\n\nfunction decodeSignedInt64(\n    values: BigUint64Array,\n    streamMetadata: StreamMetadata,\n    nullabilityBuffer?: BitVector,\n): BigInt64Array {\n    let decodedValues: BigInt64Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n                if (!nullabilityBuffer) {\n                    return decodeDeltaRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues);\n                }\n                values = decodeUnsignedRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues);\n                decodedValues = decodeZigZagDeltaInt64(values);\n            } else {\n                decodedValues = decodeZigZagDeltaInt64(values);\n            }\n            break;\n        case LogicalLevelTechnique.RLE:\n            decodedValues = decodeZigZagRleInt64(\n                values,\n                (streamMetadata as RleEncodedStreamMetadata).runs,\n                (streamMetadata as RleEncodedStreamMetadata).numRleValues,\n            );\n            break;\n        case LogicalLevelTechnique.NONE:\n            decodedValues = decodeZigZagInt64(values);\n            break;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n\n    if (nullabilityBuffer) {\n        return unpackNullable(decodedValues, nullabilityBuffer, 0n);\n    }\n    return decodedValues;\n}\n\nfunction decodeUnsignedInt64(\n    values: BigUint64Array,\n    streamMetadata: StreamMetadata,\n    nullabilityBuffer?: BitVector,\n): BigUint64Array {\n    let decodedValues: BigUint64Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n                const deltaValues = decodeUnsignedRleInt64(values, rleMetadata.runs, rleMetadata.numRleValues);\n                decodedValues = decodeUnsignedZigZagDeltaInt64(deltaValues);\n            } else {\n                decodedValues = decodeUnsignedZigZagDeltaInt64(values);\n            }\n            break;\n        case LogicalLevelTechnique.RLE:\n            decodedValues = decodeUnsignedRleInt64(\n                values,\n                (streamMetadata as RleEncodedStreamMetadata).runs,\n                (streamMetadata as RleEncodedStreamMetadata).numRleValues,\n            );\n            break;\n        case LogicalLevelTechnique.NONE:\n            decodedValues = values;\n            break;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n\n    if (nullabilityBuffer) {\n        return unpackNullable(decodedValues, nullabilityBuffer, 0n);\n    }\n    return decodedValues;\n}\n\nfunction decodeFloat64Values(values: Float64Array, streamMetadata: StreamMetadata, isSigned: boolean): Float64Array {\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n                values = decodeUnsignedRleFloat64(values, rleMetadata.runs, rleMetadata.numRleValues);\n            }\n            decodeZigZagDeltaFloat64(values);\n            return values;\n        case LogicalLevelTechnique.RLE:\n            return decodeRleFloat64(values, streamMetadata as RleEncodedStreamMetadata, isSigned);\n        case LogicalLevelTechnique.NONE:\n            if (isSigned) {\n                decodeZigZagFloat64(values);\n            }\n            return values;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n}\n\nfunction decodeLengthToOffsetBuffer(values: Uint32Array, streamMetadata: StreamMetadata): Uint32Array {\n    if (\n        streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA &&\n        streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE\n    ) {\n        return decodeZigZagDeltaOfDeltaInt32(values);\n    }\n\n    if (\n        streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.RLE &&\n        streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE\n    ) {\n        const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n        return decodeRleDeltaInt32(values, rleMetadata.runs, rleMetadata.numRleValues);\n    }\n\n    if (\n        streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.NONE &&\n        streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.NONE\n    ) {\n        //TODO: use fastInverseDelta again and check what are the performance problems in zoom 14\n        //fastInverseDelta(values);\n        inverseDelta(values);\n        const offsets = new Uint32Array(streamMetadata.numValues + 1);\n        offsets[0] = 0;\n        offsets.set(values, 1);\n        return offsets;\n    }\n\n    if (\n        streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.DELTA &&\n        streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE\n    ) {\n        const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n        const decodedValues = decodeZigZagRleDeltaInt32(values, rleMetadata.runs, rleMetadata.numRleValues);\n        fastInverseDelta(decodedValues);\n        return new Uint32Array(decodedValues);\n    }\n\n    throw new Error(\"Only delta encoding is supported for transforming length to offset streams yet.\");\n}\n\nexport function getVectorType(\n    streamMetadata: StreamMetadata,\n    sizeOrNullabilityBuffer: number | BitVector,\n    data: Uint8Array,\n    offset: IntWrapper,\n    varintWidth: \"int32\" | \"int64\" = \"int32\",\n): VectorType {\n    const logicalLevelTechnique1 = streamMetadata.logicalLevelTechnique1;\n    if (logicalLevelTechnique1 === LogicalLevelTechnique.RLE) {\n        return (streamMetadata as RleEncodedStreamMetadata).runs === 1 ? VectorType.CONST : VectorType.FLAT;\n    }\n\n    if (\n        logicalLevelTechnique1 !== LogicalLevelTechnique.DELTA ||\n        streamMetadata.logicalLevelTechnique2 !== LogicalLevelTechnique.RLE\n    ) {\n        return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;\n    }\n    const numFeatures =\n        sizeOrNullabilityBuffer instanceof BitVector ? sizeOrNullabilityBuffer.size() : sizeOrNullabilityBuffer;\n    const rleMetadata = streamMetadata as RleEncodedStreamMetadata;\n\n    if (rleMetadata.numRleValues !== numFeatures) {\n        return VectorType.FLAT;\n    }\n    // Single run is always a sequence\n    if (rleMetadata.runs === 1) {\n        return VectorType.SEQUENCE;\n    }\n\n    if (rleMetadata.runs !== 2) {\n        return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;\n    }\n    // Two runs can be a sequence if both deltas are equal to 1\n    const savedOffset = offset.get();\n\n    if (streamMetadata.physicalLevelTechnique === PhysicalLevelTechnique.VARINT) {\n        if (isDeltaRleSequenceVarintWidth(data, offset, varintWidth)) {\n            return VectorType.SEQUENCE;\n        }\n        return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;\n    }\n\n    const byteOffset = offset.get();\n    const values = new Int32Array(data.buffer, data.byteOffset + byteOffset, 4);\n    offset.set(savedOffset);\n    // Check if both deltas are encoded 1\n    const zigZagOne = 2;\n    if (values[2] === zigZagOne && values[3] === zigZagOne) {\n        return VectorType.SEQUENCE;\n    }\n    return streamMetadata.numValues === 1 ? VectorType.CONST : VectorType.FLAT;\n}\n\nfunction isDeltaRleSequenceVarintWidth(data: Uint8Array, offset: IntWrapper, varintWidth: \"int32\" | \"int64\"): boolean {\n    const peekOffset = new IntWrapper(offset.get());\n\n    if (varintWidth === \"int64\") {\n        const values = decodeVarintInt64(data, peekOffset, 4);\n        return values[2] === 2n && values[3] === 2n;\n    }\n\n    const values = decodeVarintInt32(data, peekOffset, 4);\n    return values[2] === 2 && values[3] === 2;\n}\n\nfunction decodeRleFloat64(\n    data: Float64Array,\n    streamMetadata: RleEncodedStreamMetadata,\n    isSigned: boolean,\n): Float64Array {\n    return isSigned\n        ? decodeZigZagRleFloat64(data, streamMetadata.runs, streamMetadata.numRleValues)\n        : decodeUnsignedRleFloat64(data, streamMetadata.runs, streamMetadata.numRleValues);\n}\n"
  },
  {
    "path": "ts/src/decoding/propertyDecoder.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { decodePropertyColumn } from \"./propertyDecoder\";\nimport IntWrapper from \"./intWrapper\";\nimport type Vector from \"../vector/vector\";\nimport { ScalarType, type Column } from \"../metadata/tileset/tilesetMetadata\";\nimport { Int32FlatVector } from \"../vector/flat/int32FlatVector\";\nimport { Int64FlatVector } from \"../vector/flat/int64FlatVector\";\nimport { FloatFlatVector } from \"../vector/flat/floatFlatVector\";\nimport { DoubleFlatVector } from \"../vector/flat/doubleFlatVector\";\nimport { BooleanFlatVector } from \"../vector/flat/booleanFlatVector\";\nimport { Int32SequenceVector } from \"../vector/sequence/int32SequenceVector\";\nimport { Int64SequenceVector } from \"../vector/sequence/int64SequenceVector\";\nimport { Int32ConstVector } from \"../vector/constant/int32ConstVector\";\nimport { Int64ConstVector } from \"../vector/constant/int64ConstVector\";\nimport { StringDictionaryVector } from \"../vector/dictionary/stringDictionaryVector\";\nimport { createColumnMetadataForStruct, encodeSharedDictionary, encodeStructField } from \"./decodingTestUtils\";\nimport { concatenateBuffers } from \"../encoding/encodingUtils\";\nimport {\n    encodeInt32NoneColumn,\n    encodeInt32DeltaColumn,\n    encodeInt32RleColumn,\n    encodeInt32DeltaRleColumn,\n    encodeUint32Column,\n    encodeInt64NoneColumn,\n    encodeInt64DeltaColumn,\n    encodeInt64RleColumn,\n    encodeInt64DeltaRleColumn,\n    encodeInt64NullableColumn,\n    encodeUint64Column,\n    encodeUint64NullableColumn,\n    encodeFloatColumn,\n    encodeFloatNullableColumn,\n    encodeDoubleColumn,\n    encodeDoubleNullableColumn,\n    encodeBooleanColumn,\n    encodeBooleanNullableColumn,\n    encodeInt32NullableColumn,\n} from \"../encoding/propertyEncoder\";\n\nfunction createColumnMetadata(name: string, scalarType: number, nullable = false): Column {\n    return {\n        name: name,\n        nullable: nullable,\n        type: \"scalarType\",\n        scalarType: {\n            physicalType: scalarType,\n            type: \"physicalType\",\n        },\n    };\n}\n\ndescribe(\"decodePropertyColumn - INT_32\", () => {\n    it(\"should decode INT_32 column with NONE encoding (signed)\", () => {\n        const expectedValues = new Int32Array([2, -4, 6]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32NoneColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_32 column with DELTA encoding\", () => {\n        const expectedValues = new Int32Array([2, 4, 6]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32DeltaColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_32 column with RLE encoding\", () => {\n        const expectedValues = new Int32Array([100, 100, 100, -50, -50]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32RleColumn([\n            [3, 100],\n            [2, -50],\n        ]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_32 column with DELTA+RLE encoding\", () => {\n        const expectedValues = new Int32Array([10, 12, 14, 15, 16]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32DeltaRleColumn([\n            [1, 10],\n            [2, 2],\n            [2, 1],\n        ]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode nullable INT_32 column with null values\", () => {\n        const expectedValues = [2, null, -4, null, 6];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, true);\n        const encodedData = encodeInt32NullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_32 SEQUENCE vector\", () => {\n        const numValues = 5;\n        const value = 10;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32DeltaRleColumn([[numValues, value]]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, numValues);\n\n        expect(result).toBeInstanceOf(Int32SequenceVector);\n        const seqVec = result as Int32SequenceVector;\n        expect(seqVec.getValue(0)).toBe(value);\n        expect(seqVec.getValue(1)).toBe(value + value);\n        expect(seqVec.getValue(2)).toBe(value + value * 2);\n    });\n\n    it(\"should decode INT_32 CONST vector\", () => {\n        const numValues = 5;\n        const constValue = 42;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32RleColumn([[numValues, constValue]]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, numValues);\n\n        expect(result).toBeInstanceOf(Int32ConstVector);\n        const constVec = result as Int32ConstVector;\n        expect(constVec.getValue(0)).toBe(constValue);\n        expect(constVec.getValue(4)).toBe(constValue);\n    });\n});\n\ndescribe(\"decodePropertyColumn - UINT_32\", () => {\n    it(\"should decode UINT_32 column with NONE encoding (unsigned)\", () => {\n        const expectedValues = new Uint32Array([2, 4, 6, 100]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_32, false);\n        const encodedData = encodeUint32Column(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode UINT_32 max value in FLAT vector\", () => {\n        const expectedValues = new Uint32Array([0xffffffff, 1]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_32, false);\n        const encodedData = encodeUint32Column(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        expect(resultVec.getValue(0)).toBe(0xffffffff);\n        expect(resultVec.getValue(1)).toBe(1);\n    });\n\n    it(\"should decode UINT_32 CONST vector\", () => {\n        const expectedValue = 0xffffffff;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_32, false);\n        const encodedData = encodeUint32Column(new Uint32Array([expectedValue]));\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, 1);\n\n        expect(result).toBeInstanceOf(Int32ConstVector);\n        const constVec = result as Int32ConstVector;\n        expect(constVec.getValue(0)).toBe(expectedValue);\n    });\n});\n\ndescribe(\"decodePropertyColumn - INT_64\", () => {\n    it(\"should decode INT_64 column with NONE encoding (signed)\", () => {\n        const expectedValues = new BigInt64Array([2n, -4n, 6n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64NoneColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_64 column with DELTA encoding\", () => {\n        const expectedValues = new BigInt64Array([2n, 4n, 6n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64DeltaColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_64 column with RLE encoding\", () => {\n        const expectedValues = new BigInt64Array([100n, 100n, 100n, -50n, -50n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64RleColumn([\n            [3, 100n],\n            [2, -50n],\n        ]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_64 column with DELTA+RLE encoding\", () => {\n        const expectedValues = new BigInt64Array([10n, 12n, 14n, 15n, 16n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64DeltaRleColumn([\n            [1, 10n],\n            [2, 2n],\n            [2, 1n],\n        ]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode nullable INT_64 column with null values\", () => {\n        const expectedValues = [2n, null, -4n, null, 6n];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, true);\n        const encodedData = encodeInt64NullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode INT_64 SEQUENCE vector\", () => {\n        const numValues = 5;\n        const value = 10n;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64DeltaRleColumn([[numValues, value]]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, numValues);\n\n        expect(result).toBeInstanceOf(Int64SequenceVector);\n        const seqVec = result as Int64SequenceVector;\n        expect(seqVec.getValue(0)).toBe(value);\n        expect(seqVec.getValue(1)).toBe(value + value);\n        expect(seqVec.getValue(2)).toBe(value + value * 2n);\n    });\n\n    it(\"should decode INT_64 CONST vector\", () => {\n        const numValues = 5;\n        const constValue = 42n;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.INT_64, false);\n        const encodedData = encodeInt64RleColumn([[numValues, constValue]]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, numValues);\n\n        expect(result).toBeInstanceOf(Int64ConstVector);\n        const constVec = result as Int64ConstVector;\n        expect(constVec.getValue(0)).toBe(constValue);\n        expect(constVec.getValue(4)).toBe(constValue);\n    });\n});\n\ndescribe(\"decodePropertyColumn - UINT_64\", () => {\n    it(\"should decode UINT_64 column with NONE encoding (unsigned)\", () => {\n        const expectedValues = new BigUint64Array([2n, 4n, 6n, 100n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_64, false);\n        const encodedData = encodeUint64Column(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should decode UINT_64 max value in FLAT vector\", () => {\n        const expectedValues = new BigUint64Array([0xffffffffffffffffn, 1n]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_64, false);\n        const encodedData = encodeUint64Column(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        expect(resultVec.getValue(0)).toBe(0xffffffffffffffffn);\n        expect(resultVec.getValue(1)).toBe(1n);\n    });\n\n    it(\"should decode UINT_64 CONST vector\", () => {\n        const expectedValue = 0xffffffffffffffffn;\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_64, false);\n        const encodedData = encodeUint64Column(new BigUint64Array([expectedValue]));\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, 1);\n\n        expect(result).toBeInstanceOf(Int64ConstVector);\n        const constVec = result as Int64ConstVector;\n        expect(constVec.getValue(0)).toBe(expectedValue);\n    });\n\n    it(\"should decode nullable UINT_64 column with null values\", () => {\n        const expectedValues = [2n, null, 4n, null, 6n];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.UINT_64, true);\n        const encodedData = encodeUint64NullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(Int64FlatVector);\n        const resultVec = result as Int64FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n});\n\ndescribe(\"decodePropertyColumn - FLOAT\", () => {\n    it(\"should decode non-nullable FLOAT column\", () => {\n        const expectedValues = new Float32Array([1.5, 2.7, -3.14, 4.2]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.FLOAT, false);\n        const encodedData = encodeFloatColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(FloatFlatVector);\n        const resultVec = result as FloatFlatVector;\n        expect(resultVec.size).toBe(expectedValues.length);\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBeCloseTo(expectedValues[i], 5);\n        }\n    });\n\n    it(\"should decode nullable FLOAT column with null values\", () => {\n        const expectedValues = [1.5, null, 2.7, null, 3.14];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.FLOAT, true);\n        const encodedData = encodeFloatNullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(FloatFlatVector);\n        const resultVec = result as FloatFlatVector;\n        expect(resultVec.size).toBe(expectedValues.length);\n        expect(resultVec.getValue(0)).toBeCloseTo(1.5, 5);\n        expect(resultVec.getValue(1)).toBe(null); // null value\n        expect(resultVec.getValue(2)).toBeCloseTo(2.7, 5);\n        expect(resultVec.getValue(3)).toBe(null); // null value\n        expect(resultVec.getValue(4)).toBeCloseTo(3.14, 5);\n    });\n\n    it(\"should handle offset correctly after decoding FLOAT column\", () => {\n        const expectedValues = new Float32Array([1.0, 2.0, 3.0]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.FLOAT, false);\n        const encodedData = encodeFloatColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        // Verify offset was advanced correctly\n        expect(offset.get()).toBe(encodedData.length);\n    });\n});\n\ndescribe(\"decodePropertyColumn - BOOLEAN\", () => {\n    it(\"should decode non-nullable BOOLEAN column with RLE\", () => {\n        const booleanValues = [true, false, true, true, false, false, false, true];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.BOOLEAN, false);\n        const encodedData = encodeBooleanColumn(booleanValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, booleanValues.length);\n\n        expect(result).toBeInstanceOf(BooleanFlatVector);\n        const boolVec = result as BooleanFlatVector;\n        for (let i = 0; i < booleanValues.length; i++) {\n            expect(boolVec.getValue(i)).toBe(booleanValues[i]);\n        }\n    });\n\n    it(\"should decode nullable BOOLEAN column with RLE and present stream\", () => {\n        const expectedValues = [true, null, false, null, true];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.BOOLEAN, true);\n        const encodedData = encodeBooleanNullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(BooleanFlatVector);\n        const boolVec = result as BooleanFlatVector;\n        expect(boolVec.getValue(0)).toBe(true);\n        expect(boolVec.getValue(1)).toBe(null);\n        expect(boolVec.getValue(2)).toBe(false);\n        expect(boolVec.getValue(3)).toBe(null);\n        expect(boolVec.getValue(4)).toBe(true);\n    });\n});\n\ndescribe(\"decodePropertyColumn - DOUBLE\", () => {\n    it(\"should decode non-nullable DOUBLE column\", () => {\n        const expectedValues = new Float32Array([1.2345, 5.4321, 1.33742]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.DOUBLE, false);\n        const encodedData = encodeDoubleColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        expect(result).toBeInstanceOf(DoubleFlatVector);\n        const resultVec = result as DoubleFlatVector;\n        expect(resultVec.size).toBe(expectedValues.length);\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBeCloseTo(expectedValues[i], 5);\n        }\n    });\n\n    it(\"should decode nullable DOUBLE column with null values\", () => {\n        const expectedValues = [1.5, null, 2.7, null, Math.PI];\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.DOUBLE, true);\n        const encodedData = encodeDoubleNullableColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(encodedData, offset, columnMetadata, 2, expectedValues.length);\n\n        expect(result).toBeInstanceOf(DoubleFlatVector);\n        const resultVec = result as DoubleFlatVector;\n        expect(resultVec.size).toBe(expectedValues.length);\n        expect(resultVec.getValue(0)).toBeCloseTo(1.5, 5);\n        expect(resultVec.getValue(1)).toBe(null); // null value\n        expect(resultVec.getValue(2)).toBeCloseTo(2.7, 5);\n        expect(resultVec.getValue(3)).toBe(null); // null value\n        expect(resultVec.getValue(4)).toBeCloseTo(Math.PI, 5);\n    });\n\n    it(\"should handle offset correctly after decoding DOUBLE column\", () => {\n        const expectedValues = new Float32Array([1.33742, 1.2345, 5.4321]);\n        const columnMetadata = createColumnMetadata(\"testColumn\", ScalarType.DOUBLE, false);\n        const encodedData = encodeDoubleColumn(expectedValues);\n        const offset = new IntWrapper(0);\n\n        decodePropertyColumn(encodedData, offset, columnMetadata, 1, expectedValues.length);\n\n        // Verify offset was advanced correctly\n        expect(offset.get()).toBe(encodedData.length);\n    });\n});\n\ndescribe(\"decodePropertyColumn - STRING\", () => {\n    describe(\"basic functionality\", () => {\n        it(\"should decode single field with shared dictionary\", () => {\n            const dictionaryStrings = [\"apple\", \"banana\", \"peach\", \"date\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n            const fieldStreams = encodeStructField([0, 1, 2, 3], [true, true, true, true]);\n            const completeData = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n            const columnMetadata = createColumnMetadataForStruct(\"address:\", [{ name: \"street\" }]);\n            const offset = new IntWrapper(0);\n            const result = decodePropertyColumn(completeData, offset, columnMetadata, 1, dictionaryStrings.length);\n\n            expect(result).toHaveLength(1);\n            expect((result as Vector[])[0]).toBeInstanceOf(StringDictionaryVector);\n            expect((result as Vector[])[0].name).toBe(\"address:street\");\n            for (let i = 0; i < dictionaryStrings.length; i++) {\n                expect((result as Vector[])[0].getValue(i)).toBe(dictionaryStrings[i]);\n            }\n        });\n\n        it(\"should decode shared dictionary when numStreams matches encoder output (3 + N*2)\", () => {\n            const dictionaryStrings = [\"apple\", \"banana\", \"peach\", \"date\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n            const fieldStreams = encodeStructField([0, 1, 2, 3], [true, true, true, true]);\n            const completeData = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n            const columnMetadata = createColumnMetadataForStruct(\"address:\", [{ name: \"street\" }]);\n            const offset = new IntWrapper(0);\n            const result = decodePropertyColumn(completeData, offset, columnMetadata, 5, dictionaryStrings.length);\n\n            expect(result).toHaveLength(1);\n            expect((result as Vector[])[0]).toBeInstanceOf(StringDictionaryVector);\n            expect((result as Vector[])[0].name).toBe(\"address:street\");\n            for (let i = 0; i < dictionaryStrings.length; i++) {\n                expect((result as Vector[])[0].getValue(i)).toBe(dictionaryStrings[i]);\n            }\n        });\n    });\n});\n\ndescribe(\"decodePropertyColumn - Edge Cases\", () => {\n    it(\"should filter columns with propertyColumnNames set\", () => {\n        const expectedValues = new Int32Array([1, 2, 3]);\n        const columnMetadata = createColumnMetadata(\"includedColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32NoneColumn(expectedValues);\n        const propertyColumnNames = new Set([\"includedColumn\"]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(\n            encodedData,\n            offset,\n            columnMetadata,\n            1,\n            expectedValues.length,\n            propertyColumnNames,\n        );\n\n        expect(result).toBeInstanceOf(Int32FlatVector);\n        const resultVec = result as Int32FlatVector;\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n\n    it(\"should skip column when not in propertyColumnNames filter\", () => {\n        const expectedValues = new Int32Array([1, 2, 3]);\n        const columnMetadata = createColumnMetadata(\"excludedColumn\", ScalarType.INT_32, false);\n        const encodedData = encodeInt32NoneColumn(expectedValues);\n        const propertyColumnNames = new Set([\"someOtherColumn\"]);\n        const offset = new IntWrapper(0);\n\n        const result = decodePropertyColumn(\n            encodedData,\n            offset,\n            columnMetadata,\n            1,\n            expectedValues.length,\n            propertyColumnNames,\n        );\n\n        // Should return null and advance the offset past the skipped data\n        expect(result).toBe(null);\n        expect(offset.get()).toBe(encodedData.length);\n    });\n\n    it(\"should return null for empty columns (numStreams === 0)\", () => {\n        const columnMetadata = createColumnMetadata(\"emptyColumn\", ScalarType.INT_32, false);\n        const offset = new IntWrapper(0);\n        const data = new Uint8Array(0);\n\n        const result = decodePropertyColumn(data, offset, columnMetadata, 0, 0);\n\n        expect(result).toBeNull();\n    });\n\n    it(\"should return null for complex type with numStreams === 0\", () => {\n        const columnMetadata = createColumnMetadataForStruct(\"structColumn\", [{ name: \"field1\" }, { name: \"field2\" }]);\n        const offset = new IntWrapper(0);\n        const data = new Uint8Array(0);\n\n        const result = decodePropertyColumn(data, offset, columnMetadata, 0, 5);\n\n        expect(result).toBeNull();\n    });\n\n    it(\"should throw error for unsupported data type\", () => {\n        const columnMetadata = createColumnMetadata(\"unsupportedColumn\", ScalarType.INT_8, false);\n        const encodedData = encodeInt32NoneColumn(new Int32Array([1, 2, 3]));\n        const offset = new IntWrapper(0);\n\n        expect(() => {\n            decodePropertyColumn(encodedData, offset, columnMetadata, 1, 3);\n        }).toThrow();\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/propertyDecoder.ts",
    "content": "import type IntWrapper from \"./intWrapper\";\nimport { type Column, type ScalarColumn, ScalarType } from \"../metadata/tileset/tilesetMetadata\";\nimport type Vector from \"../vector/vector\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport { decodeStreamMetadata, type RleEncodedStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport { VectorType } from \"../vector/vectorType\";\nimport { BooleanFlatVector } from \"../vector/flat/booleanFlatVector\";\nimport { DoubleFlatVector } from \"../vector/flat/doubleFlatVector\";\nimport { FloatFlatVector } from \"../vector/flat/floatFlatVector\";\nimport { Int64ConstVector } from \"../vector/constant/int64ConstVector\";\nimport { Int64FlatVector } from \"../vector/flat/int64FlatVector\";\nimport { Int32FlatVector } from \"../vector/flat/int32FlatVector\";\nimport { Int32ConstVector } from \"../vector/constant/int32ConstVector\";\nimport { decodeBooleanRle, decodeDoublesLE, decodeFloatsLE, skipColumn } from \"./decodingUtils\";\nimport {\n    decodeSignedConstInt32Stream,\n    decodeSignedConstInt64Stream,\n    decodeSignedInt32Stream,\n    decodeSignedInt64Stream,\n    decodeUnsignedInt32Stream,\n    decodeUnsignedConstInt32Stream,\n    decodeUnsignedConstInt64Stream,\n    decodeUnsignedInt64Stream,\n    decodeSequenceInt32Stream,\n    decodeSequenceInt64Stream,\n    getVectorType,\n} from \"./integerStreamDecoder\";\nimport { Int32SequenceVector } from \"../vector/sequence/int32SequenceVector\";\nimport { Int64SequenceVector } from \"../vector/sequence/int64SequenceVector\";\nimport { decodeSharedDictionary, decodeString } from \"./stringDecoder\";\n\nexport function decodePropertyColumn(\n    data: Uint8Array,\n    offset: IntWrapper,\n    columnMetadata: Column,\n    numStreams: number,\n    numFeatures: number,\n    propertyColumnNames?: Set<string>,\n): Vector | Vector[] {\n    if (columnMetadata.type === \"scalarType\") {\n        if (propertyColumnNames && !propertyColumnNames.has(columnMetadata.name)) {\n            skipColumn(numStreams, data, offset);\n            return null;\n        }\n\n        return decodeScalarPropertyColumn(\n            numStreams,\n            data,\n            offset,\n            numFeatures,\n            columnMetadata.scalarType,\n            columnMetadata,\n        );\n    }\n\n    if (numStreams === 0) {\n        return null;\n    }\n\n    return decodeSharedDictionary(data, offset, columnMetadata, propertyColumnNames);\n}\n\nfunction decodeScalarPropertyColumn(\n    numStreams: number,\n    data: Uint8Array,\n    offset: IntWrapper,\n    numFeatures: number,\n    column: ScalarColumn,\n    columnMetadata: Column,\n) {\n    let nullabilityBuffer: BitVector = null;\n    if (numStreams === 0) {\n        return null;\n    }\n\n    if (columnMetadata.nullable) {\n        const presentStreamMetadata = decodeStreamMetadata(data, offset);\n        const numValues = presentStreamMetadata.numValues;\n        const streamDataStart = offset.get();\n        const presentVector = decodeBooleanRle(data, numValues, presentStreamMetadata.byteLength, offset);\n        offset.set(streamDataStart + presentStreamMetadata.byteLength);\n        nullabilityBuffer = new BitVector(presentVector, presentStreamMetadata.numValues);\n    }\n\n    const sizeOrNullabilityBuffer = nullabilityBuffer ?? numFeatures;\n    const scalarType = column.physicalType;\n    switch (scalarType) {\n        case ScalarType.UINT_32:\n        case ScalarType.INT_32:\n            return decodeInt32Column(data, offset, columnMetadata, column, sizeOrNullabilityBuffer);\n        case ScalarType.STRING: {\n            // In embedded format: numStreams includes nullability stream if column is nullable\n            const stringDataStreams = columnMetadata.nullable ? numStreams - 1 : numStreams;\n            return decodeString(columnMetadata.name, data, offset, stringDataStreams, nullabilityBuffer);\n        }\n        case ScalarType.BOOLEAN:\n            return decodeBooleanColumn(data, offset, columnMetadata, numFeatures, sizeOrNullabilityBuffer);\n        case ScalarType.UINT_64:\n        case ScalarType.INT_64:\n            return decodeInt64Column(data, offset, columnMetadata, sizeOrNullabilityBuffer, column);\n        case ScalarType.FLOAT:\n            return decodeFloatColumn(data, offset, columnMetadata, sizeOrNullabilityBuffer);\n        case ScalarType.DOUBLE:\n            return decodeDoubleColumn(data, offset, columnMetadata, sizeOrNullabilityBuffer);\n        default:\n            throw new Error(`The specified data type for the field is currently not supported: ${column}`);\n    }\n}\n\nfunction decodeBooleanColumn(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    _numFeatures: number,\n    sizeOrNullabilityBuffer: number | BitVector,\n): BooleanFlatVector {\n    const dataStreamMetadata = decodeStreamMetadata(data, offset);\n    const numValues = dataStreamMetadata.numValues;\n    const streamDataStart = offset.get();\n    const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : undefined;\n    const dataStream = decodeBooleanRle(data, numValues, dataStreamMetadata.byteLength, offset, nullabilityBuffer);\n    offset.set(streamDataStart + dataStreamMetadata.byteLength);\n    const dataVector = new BitVector(dataStream, numValues);\n    return new BooleanFlatVector(column.name, dataVector, sizeOrNullabilityBuffer);\n}\n\nfunction decodeFloatColumn(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    sizeOrNullabilityBuffer: number | BitVector,\n): FloatFlatVector {\n    const dataStreamMetadata = decodeStreamMetadata(data, offset);\n    const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : undefined;\n    const dataStream = decodeFloatsLE(data, offset, dataStreamMetadata.numValues, nullabilityBuffer);\n    return new FloatFlatVector(column.name, dataStream, sizeOrNullabilityBuffer);\n}\n\nfunction decodeDoubleColumn(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    sizeOrNullabilityBuffer: number | BitVector,\n): DoubleFlatVector {\n    const dataStreamMetadata = decodeStreamMetadata(data, offset);\n    const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : undefined;\n    const dataStream = decodeDoublesLE(data, offset, dataStreamMetadata.numValues, nullabilityBuffer);\n    return new DoubleFlatVector(column.name, dataStream, sizeOrNullabilityBuffer);\n}\n\nfunction decodeInt64Column(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    sizeOrNullabilityBuffer: number | BitVector,\n    scalarColumn: ScalarColumn,\n): Vector<BigInt64Array | BigUint64Array, bigint> {\n    const dataStreamMetadata = decodeStreamMetadata(data, offset);\n    const vectorType = getVectorType(dataStreamMetadata, sizeOrNullabilityBuffer, data, offset, \"int64\");\n    const isSigned = scalarColumn.physicalType === ScalarType.INT_64;\n    if (vectorType === VectorType.FLAT) {\n        const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : undefined;\n        const dataStream = isSigned\n            ? decodeSignedInt64Stream(data, offset, dataStreamMetadata, nullabilityBuffer)\n            : decodeUnsignedInt64Stream(data, offset, dataStreamMetadata, nullabilityBuffer);\n        return new Int64FlatVector(column.name, dataStream, sizeOrNullabilityBuffer);\n    }\n    if (vectorType === VectorType.SEQUENCE) {\n        const id = decodeSequenceInt64Stream(data, offset, dataStreamMetadata);\n        return new Int64SequenceVector(\n            column.name,\n            id[0],\n            id[1],\n            (dataStreamMetadata as RleEncodedStreamMetadata).numRleValues,\n        );\n    }\n    const constValue = isSigned\n        ? decodeSignedConstInt64Stream(data, offset, dataStreamMetadata)\n        : decodeUnsignedConstInt64Stream(data, offset, dataStreamMetadata);\n    return new Int64ConstVector(column.name, constValue, sizeOrNullabilityBuffer, isSigned);\n}\n\nfunction decodeInt32Column(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    scalarColumn: ScalarColumn,\n    sizeOrNullabilityBuffer: number | BitVector,\n): Vector<Int32Array | Uint32Array, number> {\n    const dataStreamMetadata = decodeStreamMetadata(data, offset);\n    const vectorType = getVectorType(dataStreamMetadata, sizeOrNullabilityBuffer, data, offset);\n    const isSigned = scalarColumn.physicalType === ScalarType.INT_32;\n\n    if (vectorType === VectorType.FLAT) {\n        const nullabilityBuffer = isNullabilityBuffer(sizeOrNullabilityBuffer) ? sizeOrNullabilityBuffer : undefined;\n        const dataStream = isSigned\n            ? decodeSignedInt32Stream(data, offset, dataStreamMetadata, undefined, nullabilityBuffer)\n            : decodeUnsignedInt32Stream(data, offset, dataStreamMetadata, undefined, nullabilityBuffer);\n        return new Int32FlatVector(column.name, dataStream, sizeOrNullabilityBuffer);\n    }\n    if (vectorType === VectorType.SEQUENCE) {\n        const id = decodeSequenceInt32Stream(data, offset, dataStreamMetadata);\n        return new Int32SequenceVector(\n            column.name,\n            id[0],\n            id[1],\n            (dataStreamMetadata as RleEncodedStreamMetadata).numRleValues,\n        );\n    }\n    const constValue = isSigned\n        ? decodeSignedConstInt32Stream(data, offset, dataStreamMetadata)\n        : decodeUnsignedConstInt32Stream(data, offset, dataStreamMetadata);\n    return new Int32ConstVector(column.name, constValue, sizeOrNullabilityBuffer, isSigned);\n}\n\nfunction isNullabilityBuffer(sizeOrNullabilityBuffer: number | BitVector): sizeOrNullabilityBuffer is BitVector {\n    return sizeOrNullabilityBuffer instanceof BitVector;\n}\n"
  },
  {
    "path": "ts/src/decoding/stringDecoder.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport IntWrapper from \"./intWrapper\";\nimport { decodeString, decodeSharedDictionary } from \"./stringDecoder\";\nimport { encodePlainStrings, encodeDictionaryStrings } from \"../encoding/stringEncoder\";\nimport {\n    concatenateBuffers,\n    createColumnMetadataForStruct,\n    createStream,\n    encodeFsstStrings,\n    encodeSharedDictionary,\n    encodeStructField,\n} from \"./decodingTestUtils\";\nimport { StringFlatVector } from \"../vector/flat/stringFlatVector\";\nimport { StringDictionaryVector } from \"../vector/dictionary/stringDictionaryVector\";\nimport { StringFsstDictionaryVector } from \"../vector/fsst-dictionary/stringFsstDictionaryVector\";\nimport { ScalarType } from \"../metadata/tileset/tilesetMetadata\";\nimport { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { LengthType } from \"../metadata/tile/lengthType\";\n\ndescribe(\"decodeString - Plain String Decoder\", () => {\n    it(\"should decode plain strings with simple ASCII values\", () => {\n        const expectedStrings = [\"hello\", \"world\", \"test\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        const resultVec = result as StringFlatVector;\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode plain strings with varying lengths\", () => {\n        const expectedStrings = [\"a\", \"abc\", \"hello world\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        const resultVec = result as StringFlatVector;\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode plain strings with empty string\", () => {\n        const expectedStrings = [\"\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        const resultVec = result as StringFlatVector;\n        expect(resultVec.getValue(0)).toBe(expectedStrings[0]);\n    });\n\n    it(\"should decode plain strings with empty strings\", () => {\n        const expectedStrings = [\"\", \"encodedStrings\", \"\", \"more\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        const resultVec = result as StringFlatVector;\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode mixed null and empty strings\", () => {\n        const expectedStrings = [null, \"\", \"encodedStrings\", null, \"\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).not.toBeNull();\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(result.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode mixed ASCII and UTF-8 strings\", () => {\n        const expectedStrings = [\"hello\", \"Привет\", \"world\", \"日本\"];\n        const encodedStrings = encodePlainStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        const resultVec = result as StringFlatVector;\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n});\n\ndescribe(\"decodeString - Dictionary String Decoder\", () => {\n    it(\"should decode dictionary-compressed strings with repeated values\", () => {\n        const expectedStrings = [\"cat\", \"dog\", \"cat\", \"cat\", \"dog\"];\n        const encodedStrings = encodeDictionaryStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).toBeInstanceOf(StringDictionaryVector);\n        const resultVec = result as StringDictionaryVector;\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode dictionary with single repeated string\", () => {\n        const expectedStrings = [\"same\", \"same\", \"same\"];\n        const encodedStrings = encodeDictionaryStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).toBeInstanceOf(StringDictionaryVector);\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(result.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode dictionary with UTF-8 strings\", () => {\n        const expectedStrings = [\"café\", \"日本\", \"café\", \"日本\"];\n        const encodedStrings = encodeDictionaryStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).toBeInstanceOf(StringDictionaryVector);\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(result.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode dictionary with all unique strings\", () => {\n        const expectedStrings = [\"unique1\", \"unique2\", \"unique3\", \"unique4\"];\n        const encodedStrings = encodeDictionaryStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).toBeInstanceOf(StringDictionaryVector);\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(result.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n\n    it(\"should decode nullable dictionary strings\", () => {\n        const expectedStrings = [null, \"\", \"encodedStrings\", \"\", null];\n        const encodedStrings = encodeDictionaryStrings(expectedStrings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 4);\n\n        expect(result).toBeInstanceOf(StringDictionaryVector);\n        for (let i = 0; i < expectedStrings.length; i++) {\n            expect(result.getValue(i)).toBe(expectedStrings[i]);\n        }\n    });\n});\n\ndescribe(\"decodeString - FSST Dictionary Decoder (Basic Coverage)\", () => {\n    it(\"should decode FSST-compressed strings with simple symbol table\", () => {\n        const encodedStrings = encodeFsstStrings();\n        const offset = new IntWrapper(0);\n\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 6);\n\n        expect(result).toBeInstanceOf(StringFsstDictionaryVector);\n        const resultVec = result as StringFsstDictionaryVector;\n\n        const expectedValues = [\"cat\", \"dog\", \"cat\"];\n        for (let i = 0; i < expectedValues.length; i++) {\n            expect(resultVec.getValue(i)).toBe(expectedValues[i]);\n        }\n    });\n});\n\ndescribe(\"decodeString - Empty Column Edge Cases\", () => {\n    it(\"should handle empty column with numStreams = 0 (returns null)\", () => {\n        const fullStream = new Uint8Array([]);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", fullStream, offset, 0);\n        expect(result).toBeNull();\n    });\n\n    it(\"should handle column with all zero-length streams (returns null)\", () => {\n        const emptyStream = createStream(PhysicalStreamType.LENGTH, new Uint8Array([]), {\n            logical: { lengthType: LengthType.VAR_BINARY },\n        });\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", emptyStream, offset, 1);\n        expect(result).toBeNull();\n    });\n\n    it(\"should handle single value plain string column\", () => {\n        const strings = [\"single\"];\n        const encodedStrings = encodePlainStrings(strings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        expect((result as StringFlatVector).getValue(0)).toBe(\"single\");\n    });\n\n    it(\"should handle single null value in plain string column (returns null)\", () => {\n        const strings = [null];\n        const encodedStrings = encodePlainStrings(strings);\n        const offset = new IntWrapper(0);\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n        expect((result as StringFlatVector).getValue(0)).toBeNull();\n    });\n});\n\ndescribe(\"decodeString - Integration Tests\", () => {\n    it(\"should correctly track offset through multiple streams\", () => {\n        const strings = [\"hello\", \"world\"];\n        const encodedStrings = encodePlainStrings(strings);\n        const offset = new IntWrapper(0);\n        const initialOffset = offset.get();\n\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 2);\n\n        expect(result).toBeInstanceOf(StringFlatVector);\n        expect(offset.get()).toBeGreaterThan(initialOffset);\n        expect(offset.get()).toBe(encodedStrings.length);\n    });\n\n    it(\"should correctly track offset through nullable streams\", () => {\n        const strings = [\"test\", null, \"encodedStrings\"];\n        const encodedStrings = encodePlainStrings(strings);\n        const offset = new IntWrapper(0);\n        const initialOffset = offset.get();\n\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 3);\n\n        expect(result).not.toBeNull();\n        expect(offset.get()).toBeGreaterThan(initialOffset);\n        expect(offset.get()).toBe(encodedStrings.length);\n    });\n\n    it(\"should correctly track offset through FSST dictionary streams\", () => {\n        const encodedStrings = encodeFsstStrings();\n        const offset = new IntWrapper(0);\n        const initialOffset = offset.get();\n\n        const result = decodeString(\"testColumn\", encodedStrings, offset, 6);\n\n        expect(result).toBeInstanceOf(StringFsstDictionaryVector);\n        expect(offset.get()).toBeGreaterThan(initialOffset);\n        expect(offset.get()).toBe(encodedStrings.length);\n    });\n\n    it(\"should handle consecutive decoding operations with shared offset tracker\", () => {\n        const stream1 = encodePlainStrings([\"first\"]);\n        const stream2 = encodePlainStrings([\"second\"]);\n        const combinedStream = concatenateBuffers(stream1, stream2);\n\n        const offset = new IntWrapper(0);\n\n        const result1 = decodeString(\"column1\", combinedStream, offset, 2);\n        expect((result1 as StringFlatVector).getValue(0)).toBe(\"first\");\n\n        const offsetAfterFirst = offset.get();\n\n        const result2 = decodeString(\"column2\", combinedStream, offset, 2);\n        expect((result2 as StringFlatVector).getValue(0)).toBe(\"second\");\n\n        expect(offset.get()).toBeGreaterThan(offsetAfterFirst);\n        expect(offset.get()).toBe(combinedStream.length);\n    });\n});\n\ndescribe(\"decodeSharedDictionary\", () => {\n    describe(\"basic functionality\", () => {\n        it(\"should decode single field with shared dictionary\", () => {\n            const dictionaryStrings = [\"apple\", \"banana\", \"peach\", \"date\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const fieldStreams = encodeStructField([0, 1, 2, 3], [true, true, true, true]);\n            const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"address:\", [{ name: \"street\" }]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(1);\n            expect(result[0]).toBeInstanceOf(StringDictionaryVector);\n            expect(result[0].name).toBe(\"address:street\");\n            for (let i = 0; i < dictionaryStrings.length; i++) {\n                expect(result[0].getValue(i)).toBe(dictionaryStrings[i]);\n            }\n        });\n\n        it(\"should handle empty child field name (common prefix stripped)\", () => {\n            const dictionaryStrings = [\"Berlin\", \"London\", \"Paris\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const fieldStreams = encodeStructField([0, 1, 2], [true, true, true]);\n            const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"name\", [{ name: \"\" }]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(1);\n            expect(result[0].name).toBe(\"name\");\n            for (let i = 0; i < dictionaryStrings.length; i++) {\n                expect(result[0].getValue(i)).toBe(dictionaryStrings[i]);\n            }\n        });\n\n        it(\"should handle mix of empty and delimited child names\", () => {\n            const dict = [\"value1\", \"value2\", \"value3\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dict);\n\n            const field1 = encodeStructField([0], [true]);\n            const field2 = encodeStructField([1], [true]);\n            const field3 = encodeStructField([2], [true]);\n\n            const complete = concatenateBuffers(lengthStream, dataStream, field1, field2, field3);\n            const metadata = createColumnMetadataForStruct(\"name\", [{ name: \"\" }, { name: \":en\" }, { name: \":de\" }]);\n\n            const result = decodeSharedDictionary(complete, new IntWrapper(0), metadata);\n\n            expect(result).toHaveLength(3);\n            expect(result[0].name).toBe(\"name\");\n            expect(result[1].name).toBe(\"name:en\");\n            expect(result[2].name).toBe(\"name:de\");\n        });\n    });\n\n    describe(\"nullability\", () => {\n        it(\"should handle nullable fields with PRESENT stream\", () => {\n            const dictionaryStrings = [\"red\", \"green\", \"blue\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const fieldStreams = encodeStructField([0, 2], [true, false, true, false]);\n            const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"colors:\", [{ name: \"primary\" }]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(1);\n            const expected = [\"red\", null, \"blue\", null];\n            for (let i = 0; i < expected.length; i++) {\n                expect(result[0].getValue(i)).toBe(expected[i]);\n            }\n        });\n\n        it(\"should detect nullable fields when offsetCount < numFeatures\", () => {\n            const dictionaryStrings = [\"alpha\", \"beta\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            // Simulating implicit nullability by mismatched counts\n            const fieldStreams = encodeStructField([0, 1], [true, false, false, true]);\n            const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"greek:\", [{ name: \"letter\" }]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(1);\n            const expected = [\"alpha\", null, null, \"beta\"];\n            for (let i = 0; i < expected.length; i++) {\n                expect(result[0].getValue(i)).toBe(expected[i]);\n            }\n        });\n    });\n\n    describe(\"FSST encoding\", () => {\n        it(\"should decode FSST-compressed shared dictionary\", () => {\n            const dictionaryStrings = [\"compressed1\", \"compressed2\"];\n            const { lengthStream, dataStream, symbolLengthStream, symbolDataStream } = encodeSharedDictionary(\n                dictionaryStrings,\n                { useFsst: true },\n            );\n\n            const fieldStreams = encodeStructField([0, 1], [true, true]);\n            const completeencodedStrings = concatenateBuffers(\n                lengthStream,\n                symbolLengthStream,\n                symbolDataStream,\n                dataStream,\n                fieldStreams,\n            );\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"encodedStrings:\", [{ name: \"value\" }]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(1);\n            expect(result[0]).toBeInstanceOf(StringFsstDictionaryVector);\n            expect(result[0].name).toBe(\"encodedStrings:value\");\n        });\n    });\n\n    describe(\"field filtering\", () => {\n        it(\"should filter fields by propertyColumnNames\", () => {\n            const dictionaryStrings = [\"val1\", \"val2\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const field1Streams = encodeStructField([0], [true]);\n            const field2Streams = encodeStructField([1], [true]);\n            const field3Streams = encodeStructField([0], [true]);\n\n            const completeencodedStrings = concatenateBuffers(\n                lengthStream,\n                dataStream,\n                field1Streams,\n                field2Streams,\n                field3Streams,\n            );\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"multi:\", [\n                { name: \"field1\" },\n                { name: \"field2\" },\n                { name: \"field3\" },\n            ]);\n\n            const propertyColumnNames = new Set([\"multi:field1\", \"multi:field3\"]);\n            const result = decodeSharedDictionary(\n                completeencodedStrings,\n                new IntWrapper(0),\n                columnMetaencodedStrings,\n                propertyColumnNames,\n            );\n\n            expect(result).toHaveLength(2);\n            expect(result[0].name).toBe(\"multi:field1\");\n            expect(result[1].name).toBe(\"multi:field3\");\n        });\n\n        it(\"should skip fields with numStreams=0\", () => {\n            const dictionaryStrings = [\"present\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const field1Streams = encodeStructField([0], [true], true);\n            const field2Streams = encodeStructField([], [], false); // numStreams=0\n            const field3Streams = encodeStructField([0], [true], true);\n\n            const completeencodedStrings = concatenateBuffers(\n                lengthStream,\n                dataStream,\n                field1Streams,\n                field2Streams,\n                field3Streams,\n            );\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"test:\", [\n                { name: \"field1\" },\n                { name: \"field2\" },\n                { name: \"field3\" },\n            ]);\n\n            const result = decodeSharedDictionary(completeencodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n\n            expect(result).toHaveLength(2);\n            expect(result[0].name).toBe(\"test:field1\");\n            expect(result[1].name).toBe(\"test:field3\");\n        });\n\n        it(\"should handle mixed present and filtered fields\", () => {\n            const dictionaryStrings = [\"encodedStrings\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const field1Streams = encodeStructField([0], [true], true);\n            const field2Streams = encodeStructField([], [], false);\n            const field3Streams = encodeStructField([0], [true], true);\n\n            const completeencodedStrings = concatenateBuffers(\n                lengthStream,\n                dataStream,\n                field1Streams,\n                field2Streams,\n                field3Streams,\n            );\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"mixed:\", [\n                { name: \"field1\" },\n                { name: \"field2\" },\n                { name: \"field3\" },\n            ]);\n\n            const propertyColumnNames = new Set([\"mixed:field3\"]);\n            const result = decodeSharedDictionary(\n                completeencodedStrings,\n                new IntWrapper(0),\n                columnMetaencodedStrings,\n                propertyColumnNames,\n            );\n\n            expect(result).toHaveLength(1);\n            expect(result[0].name).toBe(\"mixed:field3\");\n        });\n    });\n\n    describe(\"error handling\", () => {\n        it(\"should throw error for non-string field types\", () => {\n            const dictionaryStrings = [\"value\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n            const fieldStreams = encodeStructField([0], [true]);\n            const completeEncodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"invalid\", [\n                { name: \"field1\", type: ScalarType.INT_32 },\n            ]);\n\n            expect(() => {\n                decodeSharedDictionary(completeEncodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n            }).toThrow(\"Currently only scalar string fields are implemented for a struct.\");\n        });\n\n        it(\"should throw error for mismatched nullability and numStreams\", () => {\n            const dictionaryStrings = [\"value\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n            const fieldStreams = encodeStructField([0], [true]);\n            const completeEncodedStrings = concatenateBuffers(lengthStream, dataStream, fieldStreams);\n\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"invalidNullability\", [\n                { name: \"field1\", type: ScalarType.STRING, nullable: false },\n            ]);\n\n            expect(() => {\n                decodeSharedDictionary(completeEncodedStrings, new IntWrapper(0), columnMetaencodedStrings);\n            }).toThrow(\n                \"The number of streams for the child field field1 does not match its nullability. nullibilty: false, numStreams: 2\",\n            );\n        });\n    });\n\n    describe(\"offset tracking\", () => {\n        it(\"should correctly advance offset through all streams\", () => {\n            const dictionaryStrings = [\"a\", \"b\", \"c\"];\n            const { lengthStream, dataStream } = encodeSharedDictionary(dictionaryStrings);\n\n            const field1Streams = encodeStructField([0, 1], [true, true]);\n            const field2Streams = encodeStructField([1, 2], [true, true]);\n\n            const completeencodedStrings = concatenateBuffers(lengthStream, dataStream, field1Streams, field2Streams);\n            const columnMetaencodedStrings = createColumnMetadataForStruct(\"track\", [\n                { name: \"field1\" },\n                { name: \"field2\" },\n            ]);\n\n            const offset = new IntWrapper(0);\n            const initialOffset = offset.get();\n            const result = decodeSharedDictionary(completeencodedStrings, offset, columnMetaencodedStrings);\n\n            expect(result).toHaveLength(2);\n            expect(offset.get()).toBeGreaterThan(initialOffset);\n            expect(offset.get()).toBe(completeencodedStrings.length);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/stringDecoder.ts",
    "content": "import { decodeStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport { StringFlatVector } from \"../vector/flat/stringFlatVector\";\nimport { StringDictionaryVector } from \"../vector/dictionary/stringDictionaryVector\";\nimport type IntWrapper from \"./intWrapper\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport type Vector from \"../vector/vector\";\nimport { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { DictionaryType } from \"../metadata/tile/dictionaryType\";\nimport { LengthType } from \"../metadata/tile/lengthType\";\nimport { decodeUnsignedInt32Stream, decodeLengthStreamToOffsetBuffer } from \"./integerStreamDecoder\";\nimport { type Column, ScalarType } from \"../metadata/tileset/tilesetMetadata\";\nimport { decodeVarintInt32 } from \"./integerDecodingUtils\";\nimport { decodeBooleanRle, skipColumn } from \"./decodingUtils\";\nimport { StringFsstDictionaryVector } from \"../vector/fsst-dictionary/stringFsstDictionaryVector\";\n\nexport function decodeString(\n    name: string,\n    data: Uint8Array,\n    offset: IntWrapper,\n    numStreams: number,\n    bitVector?: BitVector,\n): Vector {\n    let dictionaryLengthStream: Uint32Array = null;\n    let offsetStream: Uint32Array = null;\n    let dictionaryStream: Uint8Array = null;\n    let symbolLengthStream: Uint32Array = null;\n    let symbolTableStream: Uint8Array = null;\n    let nullabilityBuffer: BitVector = bitVector ?? null;\n    let plainLengthStream: Uint32Array = null;\n    let plainDataStream: Uint8Array = null;\n\n    for (let i = 0; i < numStreams; i++) {\n        const streamMetadata = decodeStreamMetadata(data, offset);\n\n        switch (streamMetadata.physicalStreamType) {\n            case PhysicalStreamType.PRESENT: {\n                const presentData = decodeBooleanRle(data, streamMetadata.numValues, streamMetadata.byteLength, offset);\n                const presentStream = new BitVector(presentData, streamMetadata.numValues);\n                nullabilityBuffer = bitVector ?? presentStream;\n                break;\n            }\n            case PhysicalStreamType.OFFSET: {\n                offsetStream = decodeUnsignedInt32Stream(data, offset, streamMetadata, undefined, nullabilityBuffer);\n                break;\n            }\n            case PhysicalStreamType.LENGTH: {\n                const lengthStream = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);\n                if (LengthType.DICTIONARY === streamMetadata.logicalStreamType.lengthType) {\n                    dictionaryLengthStream = lengthStream;\n                } else if (LengthType.SYMBOL === streamMetadata.logicalStreamType.lengthType) {\n                    symbolLengthStream = lengthStream;\n                } else {\n                    // Plain string encoding uses VAR_BINARY length type\n                    plainLengthStream = lengthStream;\n                }\n                break;\n            }\n            case PhysicalStreamType.DATA: {\n                const dataStream = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);\n                offset.add(streamMetadata.byteLength);\n                const dictType = streamMetadata.logicalStreamType.dictionaryType;\n                if (DictionaryType.FSST === dictType) {\n                    symbolTableStream = dataStream;\n                } else if (DictionaryType.SINGLE === dictType || DictionaryType.SHARED === dictType) {\n                    dictionaryStream = dataStream;\n                } else if (DictionaryType.NONE === dictType) {\n                    plainDataStream = dataStream;\n                }\n                break;\n            }\n        }\n    }\n\n    return (\n        decodeFsstDictionaryVector(\n            name,\n            symbolTableStream,\n            offsetStream,\n            dictionaryLengthStream,\n            dictionaryStream,\n            symbolLengthStream,\n            nullabilityBuffer,\n        ) ??\n        decodeDictionaryVector(name, dictionaryStream, offsetStream, dictionaryLengthStream, nullabilityBuffer) ??\n        decodePlainStringVector(name, plainLengthStream, plainDataStream, offsetStream, nullabilityBuffer)\n    );\n}\n\nfunction decodeFsstDictionaryVector(\n    name: string,\n    symbolTableStream: Uint8Array | null,\n    offsetStream: Uint32Array | null,\n    dictionaryLengthStream: Uint32Array | null,\n    dictionaryStream: Uint8Array | null,\n    symbolLengthStream: Uint32Array | null,\n    nullabilityBuffer: BitVector | null,\n): Vector | null {\n    if (!symbolTableStream) {\n        return null;\n    }\n    return new StringFsstDictionaryVector(\n        name,\n        offsetStream,\n        dictionaryLengthStream,\n        dictionaryStream,\n        symbolLengthStream,\n        symbolTableStream,\n        nullabilityBuffer,\n    );\n}\n\nfunction decodeDictionaryVector(\n    name: string,\n    dictionaryStream: Uint8Array | null,\n    offsetStream: Uint32Array | null,\n    dictionaryLengthStream: Uint32Array | null,\n    nullabilityBuffer: BitVector | null,\n): Vector | null {\n    if (!dictionaryStream) {\n        return null;\n    }\n    return nullabilityBuffer\n        ? new StringDictionaryVector(name, offsetStream, dictionaryLengthStream, dictionaryStream, nullabilityBuffer)\n        : new StringDictionaryVector(name, offsetStream, dictionaryLengthStream, dictionaryStream);\n}\n\nfunction decodePlainStringVector(\n    name: string,\n    plainLengthStream: Uint32Array | null,\n    plainDataStream: Uint8Array | null,\n    offsetStream: Uint32Array | null,\n    nullabilityBuffer: BitVector | null,\n): Vector | null {\n    if (!plainLengthStream || !plainDataStream) {\n        return null;\n    }\n\n    if (offsetStream) {\n        return nullabilityBuffer\n            ? new StringDictionaryVector(name, offsetStream, plainLengthStream, plainDataStream, nullabilityBuffer)\n            : new StringDictionaryVector(name, offsetStream, plainLengthStream, plainDataStream);\n    }\n\n    if (nullabilityBuffer && nullabilityBuffer.size() !== plainLengthStream.length - 1) {\n        const sparseOffsetStream = new Uint32Array(nullabilityBuffer.size());\n        let valueIndex = 0;\n        for (let i = 0; i < nullabilityBuffer.size(); i++) {\n            if (nullabilityBuffer.get(i)) {\n                sparseOffsetStream[i] = valueIndex++;\n            } else {\n                sparseOffsetStream[i] = 0;\n            }\n        }\n        return new StringDictionaryVector(\n            name,\n            sparseOffsetStream,\n            plainLengthStream,\n            plainDataStream,\n            nullabilityBuffer,\n        );\n    }\n\n    return nullabilityBuffer\n        ? new StringFlatVector(name, plainLengthStream, plainDataStream, nullabilityBuffer)\n        : new StringFlatVector(name, plainLengthStream, plainDataStream);\n}\n\nexport function decodeSharedDictionary(\n    data: Uint8Array,\n    offset: IntWrapper,\n    column: Column,\n    propertyColumnNames?: Set<string>,\n): Vector[] {\n    let dictionaryOffsetBuffer: Uint32Array = null;\n    let dictionaryBuffer: Uint8Array = null;\n    let symbolOffsetBuffer: Uint32Array = null;\n    let symbolTableBuffer: Uint8Array = null;\n\n    let dictionaryStreamDecoded = false;\n    while (!dictionaryStreamDecoded) {\n        const streamMetadata = decodeStreamMetadata(data, offset);\n        switch (streamMetadata.physicalStreamType) {\n            case PhysicalStreamType.LENGTH:\n                if (LengthType.DICTIONARY === streamMetadata.logicalStreamType.lengthType) {\n                    dictionaryOffsetBuffer = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);\n                } else {\n                    symbolOffsetBuffer = decodeLengthStreamToOffsetBuffer(data, offset, streamMetadata);\n                }\n                break;\n            case PhysicalStreamType.DATA:\n                if (\n                    DictionaryType.SINGLE === streamMetadata.logicalStreamType.dictionaryType ||\n                    DictionaryType.SHARED === streamMetadata.logicalStreamType.dictionaryType\n                ) {\n                    dictionaryBuffer = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);\n                    dictionaryStreamDecoded = true;\n                } else {\n                    symbolTableBuffer = data.subarray(offset.get(), offset.get() + streamMetadata.byteLength);\n                }\n                offset.add(streamMetadata.byteLength);\n                break;\n        }\n    }\n\n    const childFields = column.complexType.children;\n    const stringDictionaryVectors = [];\n    let i = 0;\n    for (const childField of childFields) {\n        const numStreams = decodeVarintInt32(data, offset, 1)[0];\n        if (numStreams === 0) {\n            /* Column is not present in the tile */\n            continue;\n        }\n\n        const columnName = childField.name ? `${column.name}${childField.name}` : column.name;\n        if (propertyColumnNames) {\n            if (!propertyColumnNames.has(columnName)) {\n                //TODO: add size of sub column to Mlt for faster skipping\n                skipColumn(numStreams, data, offset);\n                continue;\n            }\n        }\n\n        if (childField.type !== \"scalarField\" || childField.scalarField.physicalType !== ScalarType.STRING) {\n            throw new Error(\"Currently only scalar string fields are implemented for a struct.\");\n        }\n        if ((numStreams > 1 && !childField.nullable) || (numStreams === 1 && childField.nullable)) {\n            throw new Error(\n                `The number of streams for the child field ${childField.name} does not match its nullability. nullibilty: ${childField.nullable}, numStreams: ${numStreams}`,\n            );\n        }\n\n        let presentStreamBitVector: BitVector | undefined;\n        if (childField.nullable) {\n            const presentStreamMetadata = decodeStreamMetadata(data, offset);\n            const presentStream = decodeBooleanRle(\n                data,\n                presentStreamMetadata.numValues,\n                presentStreamMetadata.byteLength,\n                offset,\n            );\n            presentStreamBitVector = new BitVector(presentStream, presentStreamMetadata.numValues);\n        }\n        const offsetStreamMetadata = decodeStreamMetadata(data, offset);\n        const offsetStream = decodeUnsignedInt32Stream(\n            data,\n            offset,\n            offsetStreamMetadata,\n            undefined,\n            presentStreamBitVector,\n        );\n\n        stringDictionaryVectors[i++] = symbolTableBuffer\n            ? new StringFsstDictionaryVector(\n                  columnName,\n                  offsetStream,\n                  dictionaryOffsetBuffer,\n                  dictionaryBuffer,\n                  symbolOffsetBuffer,\n                  symbolTableBuffer,\n                  presentStreamBitVector,\n              )\n            : new StringDictionaryVector(\n                  columnName,\n                  offsetStream,\n                  dictionaryOffsetBuffer,\n                  dictionaryBuffer,\n                  presentStreamBitVector,\n              );\n    }\n\n    return stringDictionaryVectors;\n}\n"
  },
  {
    "path": "ts/src/decoding/unpackNullableUtils.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { unpackNullable, unpackNullableBoolean } from \"./unpackNullableUtils\";\nimport BitVector from \"../vector/flat/bitVector\";\nimport { packNullable, packNullableBoolean } from \"../encoding/packNullableUtils\";\n\ndescribe(\"unpackNullableUtils\", () => {\n    describe(\"unpackNullable\", () => {\n        it(\"should return the original array when presentBits is null\", () => {\n            const dataStream = new Int32Array([1, 2, 3]);\n            const packed = packNullable(dataStream, null);\n            const result = unpackNullable(packed, null, 0);\n\n            expect(result).toBe(dataStream);\n            expect(result).toEqual(new Int32Array([1, 2, 3]));\n        });\n\n        it(\"should return the original array when presentBits is undefined\", () => {\n            const dataStream = new Float32Array([1.5, 2.5, 3.5]);\n            const result = unpackNullable(dataStream, undefined, 0);\n\n            expect(result).toBe(dataStream);\n            expect(result).toEqual(new Float32Array([1.5, 2.5, 3.5]));\n        });\n\n        it(\"should return the original BigInt64Array when presentBits is null\", () => {\n            const dataStream = new BigInt64Array([10n, 20n, 30n]);\n            const result = unpackNullable(dataStream, null, 0n);\n\n            expect(result).toBe(dataStream);\n            expect(result).toEqual(new BigInt64Array([10n, 20n, 30n]));\n        });\n\n        it(\"should return the original array when presentBits is fully set\", () => {\n            const dataStream = new BigInt64Array([10n, 20n, 30n]);\n            const presentBits = new BitVector(new Uint8Array([0b111]), 3);\n            const packed = packNullable(dataStream, presentBits);\n            const result = unpackNullable(packed, presentBits, 0n);\n\n            expect(result).toEqual(new BigInt64Array([10n, 20n, 30n]));\n        });\n\n        it(\"should return partial array when presentBits is partially set\", () => {\n            const dataStream = new Int32Array([0, 20, 30]); // first number is \"null\"\n            const presentBits = new BitVector(new Uint8Array([0b110]), 3);\n            const packed = packNullable(dataStream, presentBits);\n            const result = unpackNullable(packed, presentBits, 0);\n\n            expect(result).toEqual(new Int32Array([0, 20, 30]));\n        });\n    });\n\n    describe(\"unpackNullableBoolean\", () => {\n        it(\"should return the original array when presentBits is null\", () => {\n            const dataStream = new Uint8Array([0b11010101]);\n            const packed = packNullableBoolean(dataStream, 8, null);\n            const result = unpackNullableBoolean(packed, 8, null);\n\n            expect(result).toBe(dataStream);\n            expect(result).toEqual(new Uint8Array([0b11010101]));\n        });\n\n        it(\"should return the original array when presentBits is undefined\", () => {\n            const dataStream = new Uint8Array([0b00001111]);\n            const result = unpackNullableBoolean(dataStream, 8, undefined);\n\n            expect(result).toBe(dataStream);\n            expect(result).toEqual(new Uint8Array([0b00001111]));\n        });\n\n        it(\"should return the original array when presentBits is fully set\", () => {\n            const dataStream = new Uint8Array([0b11010101]);\n            const presentBits = new BitVector(new Uint8Array([0b11111111]), 8);\n            const packed = packNullableBoolean(dataStream, 8, presentBits);\n            const result = unpackNullableBoolean(packed, 8, presentBits);\n\n            expect(result).toEqual(new Uint8Array([0b11010101]));\n        });\n\n        it(\"should return partial array when presentBits is partially set\", () => {\n            const dataStream = new Uint8Array([0b11111111]);\n            const presentBits = new BitVector(new Uint8Array([0b11110000]), 8);\n            const packed = packNullableBoolean(dataStream, 8, presentBits);\n            const result = unpackNullableBoolean(packed, 8, presentBits);\n\n            expect(result).toEqual(new Uint8Array([0b11110000]));\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/decoding/unpackNullableUtils.ts",
    "content": "import BitVector from \"../vector/flat/bitVector.js\";\n\n/**\n * Type constraint for TypedArray types that can be unpacked\n */\nexport type TypedArrayConstructor =\n    | Int32ArrayConstructor\n    | Uint32ArrayConstructor\n    | BigInt64ArrayConstructor\n    | BigUint64ArrayConstructor\n    | Float32ArrayConstructor\n    | Float64ArrayConstructor;\n\nexport type TypedArrayInstance =\n    | Int32Array\n    | Uint32Array\n    | BigInt64Array\n    | BigUint64Array\n    | Float32Array\n    | Float64Array;\n\n/**\n * Generic unpacking function.\n * Reconstructs the full array by inserting default values at null positions.\n *\n * @param dataStream The compact data stream containing only non-null values\n * @param presentBits BitVector indicating which positions have values (null if non-nullable)\n * @param defaultValue The default value to insert at null positions (0, 0n, etc.)\n * @returns Full array with default values at null positions\n */\nexport function unpackNullable<T extends TypedArrayInstance>(\n    dataStream: T,\n    presentBits: BitVector | null,\n    defaultValue: number | bigint,\n): T {\n    // Non-nullable case: return data stream as-is\n    if (!presentBits) {\n        return dataStream;\n    }\n\n    const size = presentBits.size();\n    // Create new array of same type with full size\n    const constructor = dataStream.constructor as TypedArrayConstructor;\n    const result = new constructor(size) as T;\n\n    let counter = 0;\n    for (let i = 0; i < size; i++) {\n        // If position has a value, take from data stream; otherwise use default\n        result[i] = presentBits.get(i) ? dataStream[counter++] : (defaultValue as any);\n    }\n\n    return result;\n}\n\n/**\n * Special case for boolean columns because BitVector is not directly compatible with TypedArray.\n *\n * @param dataStream The compact BitVector data containing only non-null boolean values\n * @param dataStreamSize The number of actual values in dataStream\n * @param presentBits BitVector indicating which positions have values (null if non-nullable)\n * @returns Uint8Array buffer for BitVector with false at null positions\n */\nexport function unpackNullableBoolean(\n    dataStream: Uint8Array,\n    dataStreamSize: number,\n    presentBits: BitVector | null,\n): Uint8Array {\n    // Non-nullable case\n    if (!presentBits) {\n        return dataStream;\n    }\n\n    const numFeatures = presentBits.size();\n    const bitVector = new BitVector(dataStream, dataStreamSize);\n    const result = new BitVector(new Uint8Array(Math.ceil(numFeatures / 8)), numFeatures);\n\n    let counter = 0;\n    for (let i = 0; i < numFeatures; i++) {\n        // If position has a value, take from data stream; otherwise use false\n        const value = presentBits.get(i) ? bitVector.get(counter++) : false;\n        result.set(i, value);\n    }\n\n    return result.getBuffer();\n}\n"
  },
  {
    "path": "ts/src/encoding/bigEndianEncode.ts",
    "content": "import { bswap32 } from \"../decoding/fastPforShared\";\n\n/**\n * Serializes an `Int32Array` to a big-endian byte stream.\n *\n * @param values - Int32 words to serialize.\n * @returns Big-endian byte stream (`values.length * 4` bytes).\n */\nexport function encodeBigEndianInt32s(values: Uint32Array): Uint8Array {\n    const bytes = new Uint8Array(values.length * 4);\n    const u32 = new Uint32Array(bytes.buffer, bytes.byteOffset, values.length);\n\n    for (let i = 0; i < values.length; i++) {\n        u32[i] = bswap32(values[i]);\n    }\n    return bytes;\n}\n"
  },
  {
    "path": "ts/src/encoding/constGeometryVectorEncoder.ts",
    "content": "import { ConstGeometryVector } from \"../vector/geometry/constGeometryVector\";\nimport { GEOMETRY_TYPE } from \"../vector/geometry/geometryType\";\nimport { VertexBufferType } from \"../vector/geometry/vertexBufferType\";\nimport { encodeZOrderCurve } from \"./zOrderCurveEncoder\";\nimport type { GeometryVector, MortonSettings } from \"../vector/geometry/geometryVector\";\n\nexport const DEFAULT_MORTON_SETTINGS: MortonSettings = { numBits: 16, coordinateShift: 0 } as MortonSettings;\n\nexport function encode(x: number, y: number): number {\n    return encodeZOrderCurve(x, y, DEFAULT_MORTON_SETTINGS.numBits, DEFAULT_MORTON_SETTINGS.coordinateShift);\n}\n\nexport function encodePointGeometryVector(x: number, y: number): GeometryVector {\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POINT,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0]),\n            partOffsets: new Uint32Array([0]),\n            ringOffsets: new Uint32Array([0]),\n        },\n        undefined,\n        new Int32Array([x, y]),\n    );\n}\n\nexport function encodePointGeometryVectorWithOffset(x: number, y: number): GeometryVector {\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POINT,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0]),\n            partOffsets: new Uint32Array([0]),\n            ringOffsets: new Uint32Array([0]),\n        },\n        new Uint32Array([1]),\n        new Int32Array([99, 99, x, y]),\n    );\n}\n\nexport function encodePointGeometryVectorWithMortonEncoding(x: number, y: number): GeometryVector {\n    const mortonEncoded = encode(x, y);\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POINT,\n        VertexBufferType.MORTON,\n        {\n            geometryOffsets: new Uint32Array([0]),\n            partOffsets: new Uint32Array([0]),\n            ringOffsets: new Uint32Array([0]),\n        },\n        new Uint32Array([0]),\n        new Int32Array([mortonEncoded]),\n        DEFAULT_MORTON_SETTINGS,\n    );\n}\n\nexport function encodePointsGeometryVector(points: number[]): GeometryVector {\n    return new ConstGeometryVector(\n        points.length / 2,\n        GEOMETRY_TYPE.POINT,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0]),\n            partOffsets: new Uint32Array([0]),\n            ringOffsets: new Uint32Array([0]),\n        },\n        undefined,\n        new Int32Array(points),\n    );\n}\n\nexport function encodeMultiPointGeometryVector(points: number[][]): GeometryVector {\n    const vertexBuffer = new Int32Array(points.flatMap((point) => [point[0], point[1]]));\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTIPOINT,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0, points.length]),\n            partOffsets: undefined,\n            ringOffsets: undefined,\n        },\n        undefined,\n        vertexBuffer,\n    );\n}\n\nexport function encodeLineStringGeometryVector(lines: [number, number][]): GeometryVector {\n    const vertexBuffer = new Int32Array(lines.flatMap((line) => [line[0], line[1]]));\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.LINESTRING,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: undefined,\n            partOffsets: new Uint32Array([0, vertexBuffer.length / 2]),\n            ringOffsets: undefined,\n        },\n        undefined,\n        vertexBuffer,\n    );\n}\n\nexport function encodeLineStringGeometryVectorWithMortonEncoding(line: [number, number][]): GeometryVector {\n    const numVertices = line.length;\n    const vertexBuffer = new Int32Array(numVertices);\n    const offsetBuffer = new Uint32Array(numVertices);\n    for (let i = 0; i < numVertices; i++) {\n        vertexBuffer[i] = encode(line[i][0], line[i][1]);\n        offsetBuffer[i] = i;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.LINESTRING,\n        VertexBufferType.MORTON,\n        {\n            geometryOffsets: undefined,\n            partOffsets: new Uint32Array([0, numVertices]),\n            ringOffsets: undefined,\n        },\n        offsetBuffer,\n        vertexBuffer,\n        DEFAULT_MORTON_SETTINGS,\n    );\n}\n\nexport function encodePolygonGeometryVector(polygon: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(polygon.flatMap((ring) => ring.flatMap((point) => [point[0], point[1]])));\n    const ringOffsets = new Uint32Array(polygon.length + 1);\n    ringOffsets[0] = 0;\n    let ringIndex = 1;\n    for (const ring of polygon) {\n        ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n        ringIndex++;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POLYGON,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: undefined,\n            partOffsets: new Uint32Array([0, polygon.length]),\n            ringOffsets,\n        },\n        undefined,\n        vertexBuffer,\n    );\n}\n\nexport function encodePolygonGeometryVectorWithOffsets(polygon: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(polygon.flatMap((ring) => ring.flatMap((point) => [point[0], point[1]])));\n    const ringOffsets = new Uint32Array(polygon.length + 1);\n    ringOffsets[0] = 0;\n    let ringIndex = 1;\n    for (const ring of polygon) {\n        ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n        ringIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length / 2);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POLYGON,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: undefined,\n            partOffsets: new Uint32Array([0, polygon.length]),\n            ringOffsets,\n        },\n        offsetBuffer,\n        vertexBuffer,\n    );\n}\n\nexport function encodePolygonGeometryVectorWithMortonOffsets(polygon: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(polygon.flatMap((ring) => ring.flatMap((point) => encode(point[0], point[1]))));\n    const ringOffsets = new Uint32Array(polygon.length + 1);\n    ringOffsets[0] = 0;\n    let ringIndex = 1;\n    for (const ring of polygon) {\n        ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n        ringIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.POLYGON,\n        VertexBufferType.MORTON,\n        {\n            geometryOffsets: undefined,\n            partOffsets: new Uint32Array([0, polygon.length]),\n            ringOffsets,\n        },\n        offsetBuffer,\n        vertexBuffer,\n        DEFAULT_MORTON_SETTINGS,\n    );\n}\n\nexport function encodeMultiLineStringGeometryVector(lines: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(lines.flatMap((line) => line.flatMap((point) => [point[0], point[1]])));\n    const partOffsets = new Uint32Array(lines.length + 1);\n    partOffsets[0] = 0;\n    let partIndex = 1;\n    for (const line of lines) {\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + line.length;\n        partIndex++;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTILINESTRING,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0, lines.length]),\n            partOffsets,\n            ringOffsets: undefined,\n        },\n        undefined,\n        vertexBuffer,\n    );\n}\n\nexport function encodeMultiLineStringGeometryVectorWithOffsets(lines: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(lines.flatMap((line) => line.flatMap((point) => [point[0], point[1]])));\n    const partOffsets = new Uint32Array(lines.length + 1);\n    partOffsets[0] = 0;\n    let partIndex = 1;\n    for (const line of lines) {\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + line.length;\n        partIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length / 2);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTILINESTRING,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0, lines.length]),\n            partOffsets,\n            ringOffsets: undefined,\n        },\n        offsetBuffer,\n        vertexBuffer,\n    );\n}\n\nexport function encodeMultiLineStringGeometryVectorWithMortonOffsets(lines: [number, number][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(lines.flatMap((line) => line.flatMap((point) => encode(point[0], point[1]))));\n    const partOffsets = new Uint32Array(lines.length + 1);\n    partOffsets[0] = 0;\n    let partIndex = 1;\n    for (const line of lines) {\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + line.length;\n        partIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTILINESTRING,\n        VertexBufferType.MORTON,\n        {\n            geometryOffsets: new Uint32Array([0, lines.length]),\n            partOffsets,\n            ringOffsets: undefined,\n        },\n        offsetBuffer,\n        vertexBuffer,\n        DEFAULT_MORTON_SETTINGS,\n    );\n}\n\nexport function encodeMultiPolygonGeometryVector(polygons: [number, number][][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(\n        polygons.flatMap((polygon) => polygon.flatMap((ring) => ring.flatMap((point) => [point[0], point[1]]))),\n    );\n    const ringOffsets = new Uint32Array(polygons.reduce((sum, polygon) => sum + polygon.length, 0) + 1);\n    const partOffsets = new Uint32Array(polygons.length + 1);\n    ringOffsets[0] = 0;\n    partOffsets[0] = 0;\n    let ringIndex = 1;\n    let partIndex = 1;\n    for (const polygon of polygons) {\n        for (const ring of polygon) {\n            ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n            ringIndex++;\n        }\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + polygon.length;\n        partIndex++;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTIPOLYGON,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0, polygons.length]),\n            partOffsets,\n            ringOffsets,\n        },\n        undefined,\n        vertexBuffer,\n    );\n}\n\nexport function encodeMultiPolygonGeometryVectorWithOffsets(polygons: [number, number][][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(\n        polygons.flatMap((polygon) => polygon.flatMap((ring) => ring.flatMap((point) => [point[0], point[1]]))),\n    );\n    const ringOffsets = new Uint32Array(polygons.reduce((sum, polygon) => sum + polygon.length, 0) + 1);\n    const partOffsets = new Uint32Array(polygons.length + 1);\n    ringOffsets[0] = 0;\n    partOffsets[0] = 0;\n    let ringIndex = 1;\n    let partIndex = 1;\n    for (const polygon of polygons) {\n        for (const ring of polygon) {\n            ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n            ringIndex++;\n        }\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + polygon.length;\n        partIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length / 2);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTIPOLYGON,\n        VertexBufferType.VEC_2,\n        {\n            geometryOffsets: new Uint32Array([0, polygons.length]),\n            partOffsets,\n            ringOffsets,\n        },\n        offsetBuffer,\n        vertexBuffer,\n    );\n}\n\nexport function encodeMultiPolygonGeometryVectorWithMortonOffsets(polygons: [number, number][][][]): GeometryVector {\n    const vertexBuffer = new Int32Array(\n        polygons.flatMap((polygon) => polygon.flatMap((ring) => ring.flatMap((point) => encode(point[0], point[1])))),\n    );\n    const ringOffsets = new Uint32Array(polygons.reduce((sum, polygon) => sum + polygon.length, 0) + 1);\n    const partOffsets = new Uint32Array(polygons.length + 1);\n    ringOffsets[0] = 0;\n    partOffsets[0] = 0;\n    let ringIndex = 1;\n    let partIndex = 1;\n    for (const polygon of polygons) {\n        for (const ring of polygon) {\n            ringOffsets[ringIndex] = ringOffsets[ringIndex - 1] + ring.length;\n            ringIndex++;\n        }\n        partOffsets[partIndex] = partOffsets[partIndex - 1] + polygon.length;\n        partIndex++;\n    }\n    const offsetBuffer = new Uint32Array(vertexBuffer.length);\n    for (let i = 0; i < offsetBuffer.length; i++) {\n        offsetBuffer[i] = i;\n    }\n\n    return new ConstGeometryVector(\n        1,\n        GEOMETRY_TYPE.MULTIPOLYGON,\n        VertexBufferType.MORTON,\n        {\n            geometryOffsets: new Uint32Array([0, polygons.length]),\n            partOffsets,\n            ringOffsets,\n        },\n        offsetBuffer,\n        vertexBuffer,\n        DEFAULT_MORTON_SETTINGS,\n    );\n}\n"
  },
  {
    "path": "ts/src/encoding/embeddedTilesetMetadataEncoder.ts",
    "content": "import IntWrapper from \"../decoding/intWrapper\";\nimport { encodeVarintInt32Value } from \"./integerEncodingUtils\";\nimport { concatenateBuffers } from \"../decoding/decodingTestUtils\";\n\n/**\n * Encodes a single typeCode as a varint.\n */\nexport function encodeTypeCode(typeCode: number): Uint8Array {\n    const buffer = new Uint8Array(5);\n    const offset = new IntWrapper(0);\n    encodeVarintInt32Value(typeCode, buffer, offset);\n    return buffer.slice(0, offset.get());\n}\n\n/**\n * Encodes a field name as a length-prefixed UTF-8 string.\n */\nexport function encodeFieldName(name: string): Uint8Array {\n    const textEncoder = new TextEncoder();\n    const nameBytes = textEncoder.encode(name);\n    const lengthBuf = new Uint8Array(5);\n    const offset = new IntWrapper(0);\n    encodeVarintInt32Value(nameBytes.length, lengthBuf, offset);\n    const lengthSlice = lengthBuf.slice(0, offset.get());\n    return concatenateBuffers(lengthSlice, nameBytes);\n}\n\n/**\n * Encodes a child count as a varint.\n */\nexport function encodeChildCount(count: number): Uint8Array {\n    const buffer = new Uint8Array(5);\n    const offset = new IntWrapper(0);\n    encodeVarintInt32Value(count, buffer, offset);\n    return buffer.slice(0, offset.get());\n}\n\n/**\n * Computes typeCode for a scalar field.\n */\nexport function scalarTypeCode(scalarType: number, nullable: boolean): number {\n    return 10 + scalarType * 2 + (nullable ? 1 : 0);\n}\n"
  },
  {
    "path": "ts/src/encoding/encodingUtils.ts",
    "content": "export function encodeFloatsLE(values: Float32Array): Uint8Array {\n    const buffer = new Uint8Array(values.length * 4);\n    const view = new DataView(buffer.buffer);\n\n    for (let i = 0; i < values.length; i++) {\n        view.setFloat32(i * 4, values[i], true);\n    }\n\n    return buffer;\n}\n\nexport function encodeDoubleLE(values: Float64Array): Uint8Array {\n    const buffer = new Uint8Array(values.length * Float64Array.BYTES_PER_ELEMENT);\n    const view = new DataView(buffer.buffer);\n\n    for (let i = 0; i < values.length; i++) {\n        view.setFloat64(i * Float64Array.BYTES_PER_ELEMENT, values[i], true);\n    }\n\n    return buffer;\n}\n\nexport function encodeBooleanRle(values: boolean[]): Uint8Array {\n    // Pack booleans into bytes (8 booleans per byte)\n    const numBytes = Math.ceil(values.length / 8);\n    const packed = new Uint8Array(numBytes);\n\n    for (let i = 0; i < values.length; i++) {\n        if (values[i]) {\n            const byteIndex = Math.floor(i / 8);\n            const bitIndex = i % 8;\n            packed[byteIndex] |= 1 << bitIndex;\n        }\n    }\n\n    const result = new Uint8Array(1 + numBytes);\n    result[0] = 256 - numBytes;\n    result.set(packed, 1);\n\n    return result;\n}\n\nexport function encodeByteRle(values: Uint8Array): Uint8Array {\n    const result: number[] = [];\n    let i = 0;\n\n    while (i < values.length) {\n        const currentByte = values[i];\n        let runLength = 1;\n\n        while (i + runLength < values.length && values[i + runLength] === currentByte && runLength < 131) {\n            runLength++;\n        }\n\n        if (runLength >= 3) {\n            const header = runLength - 3;\n            result.push(Math.min(header, 0x7f));\n            result.push(currentByte);\n            i += runLength;\n        } else {\n            const literalStart = i;\n            while (i < values.length) {\n                let nextRunLength = 1;\n                if (i + 1 < values.length) {\n                    while (\n                        i + nextRunLength < values.length &&\n                        values[i + nextRunLength] === values[i] &&\n                        nextRunLength < 3\n                    ) {\n                        nextRunLength++;\n                    }\n                }\n\n                if (nextRunLength >= 3) {\n                    break;\n                }\n                i++;\n\n                if (i - literalStart >= 128) {\n                    break;\n                }\n            }\n\n            const numLiterals = i - literalStart;\n            const header = 256 - numLiterals;\n            result.push(header);\n            for (let j = literalStart; j < i; j++) {\n                result.push(values[j]);\n            }\n        }\n    }\n\n    return new Uint8Array(result);\n}\n\nexport function encodeStrings(strings: string[]): Uint8Array {\n    const encoder = new TextEncoder();\n    const encoded = strings.map((s) => encoder.encode(s));\n    const totalLength = encoded.reduce((sum, arr) => sum + arr.length, 0);\n    const result = new Uint8Array(totalLength);\n    let offset = 0;\n    for (const arr of encoded) {\n        result.set(arr, offset);\n        offset += arr.length;\n    }\n    return result;\n}\n\nexport function createStringLengths(strings: string[]): Uint32Array {\n    const lengths = new Uint32Array(strings.length);\n    const encoder = new TextEncoder();\n    for (let i = 0; i < strings.length; i++) {\n        lengths[i] = encoder.encode(strings[i]).length;\n    }\n    return lengths;\n}\n\nexport function concatenateBuffers(...buffers: Uint8Array[]): Uint8Array {\n    const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0);\n    const result = new Uint8Array(totalLength);\n    let offset = 0;\n\n    for (const buffer of buffers) {\n        result.set(buffer, offset);\n        offset += buffer.length;\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "ts/src/encoding/fastPforEncoder.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\n\nimport { decodeFastPforInt32 } from \"../decoding/fastPforDecoder\";\nimport { BLOCK_SIZE } from \"../decoding/fastPforShared\";\nimport { createFastPforEncoderWorkspace, encodeFastPforInt32WithWorkspace } from \"./fastPforEncoder\";\n\nconst GROWTH_MULTIPLIER = 3;\nconst BASE_ALTERNATING_MASK = 1;\nconst EXCEPTION_POS_A = 10;\nconst EXCEPTION_POS_B = 100;\nconst EXCEPTION_OUTLIER_VALUE = 7;\nconst UNDERSIZED_PREALLOCATED_STREAM = 1;\nconst LARGE_INCOMPRESSIBLE_BLOCK_COUNT = 2050;\n\ndescribe(\"FastPFOR encoder\", () => {\n    it(\"grows byteContainer when workspace capacity is too small\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i * GROWTH_MULTIPLIER;\n\n        const workspace = createFastPforEncoderWorkspace();\n        workspace.byteContainer = new Uint8Array(0);\n\n        const encoded = encodeFastPforInt32WithWorkspace(values, workspace);\n        const decoded = decodeFastPforInt32(encoded, values.length);\n\n        expect(decoded).toEqual(values);\n        expect(workspace.byteContainer.length).toBeGreaterThan(0);\n    });\n\n    it(\"grows exception buffer when preallocated exception stream is too small\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i & BASE_ALTERNATING_MASK;\n        values[EXCEPTION_POS_A] = EXCEPTION_OUTLIER_VALUE;\n        values[EXCEPTION_POS_B] = EXCEPTION_OUTLIER_VALUE;\n\n        const workspace = createFastPforEncoderWorkspace();\n        workspace.dataToBePacked[2] = new Uint32Array(UNDERSIZED_PREALLOCATED_STREAM);\n\n        const encoded = encodeFastPforInt32WithWorkspace(values, workspace);\n        const decoded = decodeFastPforInt32(encoded, values.length);\n\n        expect(decoded).toEqual(values);\n        const exceptionStream = workspace.dataToBePacked[2];\n        expect(exceptionStream).toBeDefined();\n        expect(exceptionStream?.length).toBeGreaterThan(1);\n    });\n\n    it(\"rounds grown exception buffers to a multiple of 32\", () => {\n        const values = new Uint32Array(BLOCK_SIZE);\n        for (let i = 0; i < values.length; i++) values[i] = i & BASE_ALTERNATING_MASK;\n        values[EXCEPTION_POS_A] = EXCEPTION_OUTLIER_VALUE;\n        values[EXCEPTION_POS_B] = EXCEPTION_OUTLIER_VALUE;\n\n        const workspace = createFastPforEncoderWorkspace();\n        workspace.dataToBePacked[2] = new Uint32Array(UNDERSIZED_PREALLOCATED_STREAM);\n\n        encodeFastPforInt32WithWorkspace(values, workspace);\n\n        const exceptionStream = workspace.dataToBePacked[2];\n        expect(exceptionStream).toBeDefined();\n        const exceptionStreamLength = exceptionStream?.length;\n        expect(exceptionStreamLength).toBeDefined();\n        expect((exceptionStreamLength ?? 0) % 32).toBe(0);\n    });\n\n    it(\"round-trips a large incompressible payload\", () => {\n        const values = new Uint32Array(BLOCK_SIZE * LARGE_INCOMPRESSIBLE_BLOCK_COUNT);\n        for (let i = 0; i < values.length; i++) values[i] = -(i + 1);\n\n        const encoded = encodeFastPforInt32WithWorkspace(values, createFastPforEncoderWorkspace());\n        const decoded = decodeFastPforInt32(encoded, values.length);\n\n        expect(decoded).toEqual(values);\n    });\n});\n"
  },
  {
    "path": "ts/src/encoding/fastPforEncoder.ts",
    "content": "import {\n    MASKS,\n    DEFAULT_PAGE_SIZE,\n    BLOCK_SIZE,\n    greatestMultiple,\n    roundUpToMultipleOf32,\n    normalizePageSize,\n} from \"../decoding/fastPforShared\";\n\nconst EXCEPTION_OVERHEAD_BITS = 8;\nconst MAX_BIT_WIDTH = 32;\nconst BIT_WIDTH_SLOTS = MAX_BIT_WIDTH + 1;\nconst PAGE_SIZE = normalizePageSize(DEFAULT_PAGE_SIZE);\nconst INITIAL_PACKED_BUFFER_SIZE_WORDS = (PAGE_SIZE / 32) * 4;\nconst BYTE_CONTAINER_SIZE = ((3 * PAGE_SIZE) / BLOCK_SIZE + PAGE_SIZE) | 0;\n\nfunction requiredBits(value: number): number {\n    return 32 - Math.clz32(value >>> 0);\n}\n\nfunction ensureInt32Capacity(buffer: Uint32Array, requiredLength: number): Uint32Array {\n    if (requiredLength <= buffer.length) return buffer;\n\n    let newLength = buffer.length === 0 ? 1 : buffer.length;\n    while (newLength < requiredLength) {\n        newLength *= 2;\n    }\n\n    const next = new Uint32Array(newLength);\n    next.set(buffer);\n    return next;\n}\n\nfunction ensureUint8Capacity(buffer: Uint8Array, requiredLength: number): Uint8Array {\n    if (requiredLength <= buffer.length) return buffer;\n\n    let newLength = buffer.length === 0 ? 1 : buffer.length;\n    while (newLength < requiredLength) {\n        newLength *= 2;\n    }\n\n    const next = new Uint8Array(newLength);\n    next.set(buffer);\n    return next;\n}\n\n/**\n * Internal workspace for the FastPFOR encoder.\n * Exposed so callers can avoid allocations.\n * Use one workspace per concurrent encode call.\n */\nexport type FastPforEncoderWorkspace = {\n    dataToBePacked: Array<Uint32Array | undefined>;\n    dataPointers: Int32Array;\n    byteContainer: Uint8Array;\n    bitWidthFrequencies: Int32Array;\n    bestBitWidthPlan: Int32Array;\n};\n\nexport function fastPack32(\n    inValues: Uint32Array,\n    inPos: number,\n    out: Uint32Array,\n    outPos: number,\n    bitWidth: number,\n): void {\n    if (bitWidth === 0) return;\n    if (bitWidth === 32) {\n        out.set(inValues.subarray(inPos, inPos + 32), outPos);\n        return;\n    }\n\n    const mask = MASKS[bitWidth] >>> 0;\n    let outputWordIndex = outPos;\n    let bitOffset = 0;\n    let currentWord = 0;\n\n    for (let i = 0; i < 32; i++) {\n        const value = (inValues[inPos + i] >>> 0) & mask;\n\n        if (bitOffset + bitWidth <= 32) {\n            currentWord |= value << bitOffset;\n            bitOffset += bitWidth;\n\n            if (bitOffset === 32) {\n                out[outputWordIndex++] = currentWord | 0;\n                bitOffset = 0;\n                currentWord = 0;\n            }\n        } else {\n            const lowBits = 32 - bitOffset;\n            const lowMask = MASKS[lowBits] >>> 0;\n            currentWord |= (value & lowMask) << bitOffset;\n            out[outputWordIndex++] = currentWord | 0;\n            currentWord = value >>> lowBits;\n            bitOffset = bitWidth - lowBits;\n        }\n    }\n}\n\nexport function createFastPforEncoderWorkspace(): FastPforEncoderWorkspace {\n    const dataToBePacked: Array<Uint32Array | undefined> = new Array(BIT_WIDTH_SLOTS);\n    for (let k = 1; k < BIT_WIDTH_SLOTS; k++) {\n        dataToBePacked[k] = new Uint32Array(INITIAL_PACKED_BUFFER_SIZE_WORDS);\n    }\n\n    return {\n        dataToBePacked,\n        dataPointers: new Int32Array(BIT_WIDTH_SLOTS),\n        byteContainer: new Uint8Array(BYTE_CONTAINER_SIZE),\n        bitWidthFrequencies: new Int32Array(BIT_WIDTH_SLOTS),\n        bestBitWidthPlan: new Int32Array(3),\n    };\n}\n\nfunction computeBestBitWidthPlan(inValues: Uint32Array, pos: number, workspace: FastPforEncoderWorkspace): void {\n    const bitWidthFrequencies = workspace.bitWidthFrequencies;\n    const bestBitWidthPlan = workspace.bestBitWidthPlan;\n    bitWidthFrequencies.fill(0);\n    for (let k = pos, kEnd = pos + BLOCK_SIZE; k < kEnd; k++) {\n        bitWidthFrequencies[requiredBits(inValues[k])]++;\n    }\n\n    let maxBitWidth = MAX_BIT_WIDTH;\n    while (bitWidthFrequencies[maxBitWidth] === 0) maxBitWidth--;\n\n    let bestBitWidth = maxBitWidth;\n    let bestCost = maxBitWidth * BLOCK_SIZE;\n    let exceptionCount = 0;\n    let bestExceptionCount = exceptionCount;\n\n    for (let candidateBitWidth = maxBitWidth - 1; candidateBitWidth >= 0; candidateBitWidth--) {\n        exceptionCount += bitWidthFrequencies[candidateBitWidth + 1];\n        if (exceptionCount === BLOCK_SIZE) break;\n\n        let candidateCost =\n            exceptionCount * EXCEPTION_OVERHEAD_BITS +\n            exceptionCount * (maxBitWidth - candidateBitWidth) +\n            candidateBitWidth * BLOCK_SIZE +\n            8;\n        if (maxBitWidth - candidateBitWidth === 1) candidateCost -= exceptionCount;\n\n        if (candidateCost < bestCost) {\n            bestCost = candidateCost;\n            bestBitWidth = candidateBitWidth;\n            bestExceptionCount = exceptionCount;\n        }\n    }\n\n    bestBitWidthPlan[0] = bestBitWidth;\n    bestBitWidthPlan[1] = bestExceptionCount;\n    bestBitWidthPlan[2] = maxBitWidth;\n}\n\nfunction writeByte(workspace: FastPforEncoderWorkspace, byteContainerPos: number, byteValue: number): number {\n    if (byteContainerPos >= workspace.byteContainer.length) {\n        workspace.byteContainer = ensureUint8Capacity(workspace.byteContainer, byteContainerPos + 1);\n    }\n    workspace.byteContainer[byteContainerPos] = byteValue & 0xff;\n    return byteContainerPos + 1;\n}\n\nfunction ensureExceptionValuesCapacity(\n    dataToBePacked: Array<Uint32Array | undefined>,\n    dataPointers: Int32Array,\n    exceptionBitWidth: number,\n    exceptionCount: number,\n): void {\n    if (exceptionBitWidth === 1) return;\n\n    const needed = dataPointers[exceptionBitWidth] + exceptionCount;\n    const currentExceptionValues = dataToBePacked[exceptionBitWidth];\n    if (!currentExceptionValues || needed >= currentExceptionValues.length) {\n        let newSize = 2 * needed;\n        newSize = roundUpToMultipleOf32(newSize);\n        const next = new Uint32Array(newSize);\n        if (currentExceptionValues) next.set(currentExceptionValues);\n        dataToBePacked[exceptionBitWidth] = next;\n    }\n}\n\nfunction writeBlockHeader(\n    workspace: FastPforEncoderWorkspace,\n    byteContainerPos: number,\n    bitWidth: number,\n    exceptionCount: number,\n    maxBitWidth: number,\n): number {\n    byteContainerPos = writeByte(workspace, byteContainerPos, bitWidth);\n    byteContainerPos = writeByte(workspace, byteContainerPos, exceptionCount);\n    if (exceptionCount > 0) {\n        byteContainerPos = writeByte(workspace, byteContainerPos, maxBitWidth);\n    }\n    return byteContainerPos;\n}\n\nfunction recordBlockExceptions(\n    workspace: FastPforEncoderWorkspace,\n    inValues: Uint32Array,\n    blockPos: number,\n    bitWidth: number,\n    exceptionCount: number,\n    exceptionBitWidth: number,\n    byteContainerPos: number,\n): number {\n    if (exceptionCount === 0) return byteContainerPos;\n\n    const dataToBePacked = workspace.dataToBePacked;\n    const dataPointers = workspace.dataPointers;\n\n    ensureExceptionValuesCapacity(dataToBePacked, dataPointers, exceptionBitWidth, exceptionCount);\n\n    for (let k = 0; k < BLOCK_SIZE; k++) {\n        const value = inValues[blockPos + k] >>> 0;\n        if (value >>> bitWidth !== 0) {\n            byteContainerPos = writeByte(workspace, byteContainerPos, k);\n            if (exceptionBitWidth !== 1) {\n                const exceptionValues = dataToBePacked[exceptionBitWidth];\n                exceptionValues[dataPointers[exceptionBitWidth]++] = (value >>> bitWidth) | 0;\n            }\n        }\n    }\n\n    return byteContainerPos;\n}\n\ntype EncodeState = { inPos: number; out: Uint32Array; outPos: number };\n\nfunction packBlock(inValues: Uint32Array, blockPos: number, bitWidth: number, state: EncodeState): void {\n    for (let k = 0; k < BLOCK_SIZE; k += 32) {\n        state.out = ensureInt32Capacity(state.out, state.outPos + bitWidth);\n        fastPack32(inValues, blockPos + k, state.out, state.outPos, bitWidth);\n        state.outPos += bitWidth;\n    }\n}\n\nfunction padByteContainerToInt32(workspace: FastPforEncoderWorkspace, byteContainerPos: number): number {\n    while ((byteContainerPos & 3) !== 0) {\n        byteContainerPos = writeByte(workspace, byteContainerPos, 0);\n    }\n    return byteContainerPos;\n}\n\nfunction writeByteContainerInts(\n    workspace: FastPforEncoderWorkspace,\n    state: EncodeState,\n    byteContainerPos: number,\n): void {\n    const howManyInts = byteContainerPos / 4;\n    state.out = ensureInt32Capacity(state.out, state.outPos + howManyInts);\n\n    const byteContainer = workspace.byteContainer;\n    for (let i = 0; i < howManyInts; i++) {\n        const base = i * 4;\n        const packedWord =\n            byteContainer[base] |\n            (byteContainer[base + 1] << 8) |\n            (byteContainer[base + 2] << 16) |\n            (byteContainer[base + 3] << 24) |\n            0;\n        state.out[state.outPos + i] = packedWord;\n    }\n\n    state.outPos += howManyInts;\n}\n\nfunction computeExceptionBitmap(dataPointers: Int32Array): number {\n    let bitmap = 0;\n    for (let k = 2; k <= MAX_BIT_WIDTH; k++) {\n        if (dataPointers[k] !== 0) {\n            bitmap |= k === MAX_BIT_WIDTH ? 0x80000000 : 1 << (k - 1);\n        }\n    }\n    return bitmap;\n}\n\nfunction writeExceptionStreams(workspace: FastPforEncoderWorkspace, state: EncodeState): void {\n    const dataPointers = workspace.dataPointers;\n    const dataToBePacked = workspace.dataToBePacked;\n\n    const bitmap = computeExceptionBitmap(dataPointers);\n    state.out = ensureInt32Capacity(state.out, state.outPos + 1);\n    state.out[state.outPos++] = bitmap;\n\n    for (let k = 2; k <= MAX_BIT_WIDTH; k++) {\n        const size = dataPointers[k];\n        if (size !== 0) {\n            state.out = ensureInt32Capacity(state.out, state.outPos + 1);\n            state.out[state.outPos++] = size | 0;\n\n            let j = 0;\n            for (; j < size; j += 32) {\n                const exceptionValues = dataToBePacked[k];\n                state.out = ensureInt32Capacity(state.out, state.outPos + k);\n                fastPack32(exceptionValues, j, state.out, state.outPos, k);\n                state.outPos += k;\n            }\n\n            const overflow = j - size;\n            state.outPos -= (overflow * k) >>> 5;\n        }\n    }\n}\n\nfunction encodePage(\n    inValues: Uint32Array,\n    thisSize: number,\n    state: EncodeState,\n    workspace: FastPforEncoderWorkspace,\n): void {\n    const headerPos = state.outPos;\n    state.out = ensureInt32Capacity(state.out, headerPos + 1);\n    state.outPos = (state.outPos + 1) | 0;\n\n    const dataPointers = workspace.dataPointers;\n    dataPointers.fill(0);\n\n    let byteContainerPos = 0;\n\n    let tmpInPos = state.inPos;\n    const finalInPos = tmpInPos + thisSize - BLOCK_SIZE;\n\n    for (; tmpInPos <= finalInPos; tmpInPos += BLOCK_SIZE) {\n        computeBestBitWidthPlan(inValues, tmpInPos, workspace);\n\n        const bestBitWidthPlan = workspace.bestBitWidthPlan;\n        const bitWidth = bestBitWidthPlan[0];\n        const exceptionCount = bestBitWidthPlan[1];\n        const maxBitWidth = bestBitWidthPlan[2];\n\n        const exceptionBitWidth = exceptionCount > 0 ? maxBitWidth - bitWidth : 0;\n        byteContainerPos = writeBlockHeader(workspace, byteContainerPos, bitWidth, exceptionCount, maxBitWidth);\n        byteContainerPos = recordBlockExceptions(\n            workspace,\n            inValues,\n            tmpInPos,\n            bitWidth,\n            exceptionCount,\n            exceptionBitWidth,\n            byteContainerPos,\n        );\n\n        packBlock(inValues, tmpInPos, bitWidth, state);\n    }\n\n    const pageEndOutPos = state.outPos;\n    state.inPos = tmpInPos;\n    state.out[headerPos] = (pageEndOutPos - headerPos) | 0;\n\n    const byteSize = byteContainerPos;\n    byteContainerPos = padByteContainerToInt32(workspace, byteContainerPos);\n\n    state.out = ensureInt32Capacity(state.out, state.outPos + 1);\n    state.out[state.outPos++] = byteSize | 0;\n\n    writeByteContainerInts(workspace, state, byteContainerPos);\n\n    writeExceptionStreams(workspace, state);\n}\n\nfunction encodeAlignedPages(\n    inValues: Uint32Array,\n    inLength: number,\n    state: EncodeState,\n    workspace: FastPforEncoderWorkspace,\n): void {\n    const alignedLength = greatestMultiple(inLength, BLOCK_SIZE);\n    const finalInPos = state.inPos + alignedLength;\n\n    while (state.inPos !== finalInPos) {\n        const thisSize = Math.min(PAGE_SIZE, finalInPos - state.inPos);\n        encodePage(inValues, thisSize, state, workspace);\n    }\n}\n\nfunction encode(\n    inValues: Uint32Array,\n    inLength: number,\n    state: EncodeState,\n    workspace: FastPforEncoderWorkspace,\n): void {\n    const alignedLength = greatestMultiple(inLength, BLOCK_SIZE);\n    state.out = ensureInt32Capacity(state.out, state.outPos + 1);\n    state.out[state.outPos++] = alignedLength;\n\n    if (alignedLength === 0) return;\n    encodeAlignedPages(inValues, alignedLength, state, workspace);\n}\n\n/**\n * VByte encoding for FastPFOR tail values (MSB=1 terminator).\n * Note: Inverts standard Protobuf Varint (MSB=0 terminator), so we cannot reuse generic methods.\n */\nfunction encodeVByte(\n    inValues: Uint32Array,\n    inLength: number,\n    state: EncodeState,\n    workspace: FastPforEncoderWorkspace,\n): void {\n    if (inLength === 0) return;\n\n    const requiredBytes = inLength * 5 + 3;\n    workspace.byteContainer = ensureUint8Capacity(workspace.byteContainer, requiredBytes);\n\n    const start = state.inPos;\n    let bytePos = 0;\n    for (let k = start; k < start + inLength; k++) {\n        let value = inValues[k] >>> 0;\n        while (value >= 0x80) {\n            workspace.byteContainer[bytePos++] = value & 0x7f;\n            value >>>= 7;\n        }\n        workspace.byteContainer[bytePos++] = (value | 0x80) & 0xff;\n    }\n\n    while ((bytePos & 3) !== 0) workspace.byteContainer[bytePos++] = 0;\n\n    const intsToWrite = bytePos / 4;\n    state.out = ensureInt32Capacity(state.out, state.outPos + intsToWrite);\n\n    let outIdx = state.outPos;\n    for (let i = 0; i < bytePos; i += 4) {\n        const packedWord =\n            workspace.byteContainer[i] |\n            (workspace.byteContainer[i + 1] << 8) |\n            (workspace.byteContainer[i + 2] << 16) |\n            (workspace.byteContainer[i + 3] << 24) |\n            0;\n        state.out[outIdx++] = packedWord;\n    }\n\n    state.outPos = outIdx;\n    state.inPos = (state.inPos + inLength) | 0;\n}\n\n/**\n * Encodes an int32 stream using the FastPFOR wire format (pages + VByte tail).\n */\nexport function encodeFastPforInt32WithWorkspace(\n    values: Uint32Array,\n    workspace: FastPforEncoderWorkspace,\n): Uint32Array {\n    const state: EncodeState = { inPos: 0, outPos: 0, out: new Uint32Array(values.length + 1024) };\n\n    encode(values, values.length, state, workspace);\n\n    const remaining = values.length - state.inPos;\n    encodeVByte(values, remaining, state, workspace);\n\n    return state.out.subarray(0, state.outPos);\n}\n"
  },
  {
    "path": "ts/src/encoding/fsstEncoder.ts",
    "content": "/**\n * Create symbol table from string array\n *\n * @param symbolStrings     Array of symbol strings\n * @returns                 Symbol table buffer and lengths\n */\nexport function createSymbolTable(symbolStrings: string[]): { symbols: Uint8Array; symbolLengths: Uint32Array } {\n    const textEncoder = new TextEncoder();\n    const symbolBuffers = symbolStrings.map((s) => textEncoder.encode(s));\n    const symbolLengths = new Uint32Array(symbolBuffers.map((b) => b.length));\n    const totalLength = symbolBuffers.reduce((sum, b) => sum + b.length, 0);\n    const symbols = new Uint8Array(totalLength);\n\n    let offset = 0;\n    for (const buffer of symbolBuffers) {\n        symbols.set(buffer, offset);\n        offset += buffer.length;\n    }\n\n    return { symbols, symbolLengths };\n}\n\n/**\n * Encode data using FSST compression with pre-defined symbol table\n * Encoder requires pre-defined symbol table. Real FSST learns optimal symbols from data. This\n * implementation is for testing decoder only.\n *\n * @param symbols           Array of symbols, where each symbol can be between 1 and 8 bytes\n * @param symbolLengths     Array of symbol lengths, length of each symbol in symbols array\n * @param uncompressedData  Data to compress\n * @returns                 FSST compressed data, where each entry is an index to the symbols array\n */\nexport function encodeFsst(symbols: Uint8Array, symbolLengths: Uint32Array, uncompressedData: Uint8Array): Uint8Array {\n    if (uncompressedData.length === 0) {\n        return new Uint8Array(0);\n    }\n\n    // Calculate symbol offsets (cumulative sum of lengths)\n    const symbolOffsets: number[] = new Array(symbolLengths.length).fill(0);\n    for (let i = 1; i < symbolLengths.length; i++) {\n        symbolOffsets[i] = symbolOffsets[i - 1] + symbolLengths[i - 1];\n    }\n\n    const result: number[] = [];\n    let pos = 0;\n\n    while (pos < uncompressedData.length) {\n        let bestSymbolIndex = -1;\n        let bestSymbolLength = 0;\n\n        // Try to find longest matching symbol at current position\n        for (let symbolIndex = 0; symbolIndex < symbolLengths.length; symbolIndex++) {\n            const symbolLength = symbolLengths[symbolIndex];\n            const symbolOffset = symbolOffsets[symbolIndex];\n\n            // Check if symbol could fit and is longer than current best\n            if (pos + symbolLength <= uncompressedData.length && symbolLength > bestSymbolLength) {\n                // Check if bytes match\n                let matches = true;\n                for (let i = 0; i < symbolLength; i++) {\n                    if (symbols[symbolOffset + i] !== uncompressedData[pos + i]) {\n                        matches = false;\n                        break;\n                    }\n                }\n\n                if (matches) {\n                    bestSymbolIndex = symbolIndex;\n                    bestSymbolLength = symbolLength;\n                }\n            }\n        }\n\n        if (bestSymbolIndex !== -1) {\n            // Found a matching symbol\n            result.push(bestSymbolIndex);\n            pos += bestSymbolLength;\n        } else {\n            // No match - emit escape sequence (255 followed by literal byte)\n            result.push(255);\n            result.push(uncompressedData[pos]);\n            pos++;\n        }\n    }\n\n    return new Uint8Array(result);\n}\n"
  },
  {
    "path": "ts/src/encoding/integerEncodingUtils.ts",
    "content": "import IntWrapper from \"../decoding/intWrapper\";\nimport { createFastPforEncoderWorkspace, encodeFastPforInt32WithWorkspace } from \"./fastPforEncoder\";\nimport { encodeBigEndianInt32s } from \"./bigEndianEncode\";\n\nexport function encodeVarintInt32Value(value: number, dst: Uint8Array, offset: IntWrapper): void {\n    let v = value;\n    while (v > 0x7f) {\n        dst[offset.get()] = (v & 0x7f) | 0x80;\n        offset.increment();\n        v >>>= 7;\n    }\n    dst[offset.get()] = v & 0x7f;\n    offset.increment();\n}\n\nexport function encodeVarintInt32(values: Uint32Array): Uint8Array {\n    const buffer = new Uint8Array(values.length * 5);\n    const offset = new IntWrapper(0);\n\n    for (const value of values) {\n        encodeVarintInt32Value(value, buffer, offset);\n    }\n    return buffer.slice(0, offset.get());\n}\n\nexport function encodeVarintInt64(values: BigUint64Array): Uint8Array {\n    const buffer = new Uint8Array(values.length * 10);\n    const offset = new IntWrapper(0);\n\n    for (const value of values) {\n        encodeVarintInt64Value(value, buffer, offset);\n    }\n    return buffer.slice(0, offset.get());\n}\n\nfunction encodeVarintInt64Value(value: bigint, dst: Uint8Array, offset: IntWrapper): void {\n    let v = value;\n    while (v > 0x7fn) {\n        dst[offset.get()] = Number(v & 0x7fn) | 0x80;\n        offset.increment();\n        v >>= 7n;\n    }\n    dst[offset.get()] = Number(v & 0x7fn);\n    offset.increment();\n}\n\nexport function encodeVarintFloat64(values: Float64Array): Uint8Array {\n    // 1. Calculate the exact size required for the buffer\n    let size = 0;\n    for (let i = 0; i < values.length; i++) {\n        let val = values[i];\n        // Ensure we handle the value as a positive integer\n        val = val < 0 ? 0 : Math.floor(val);\n\n        // 0 always takes 1 byte\n        if (val === 0) {\n            size++;\n            continue;\n        }\n\n        // Calculate bytes needed: ceil(log128(val + 1))\n        while (val > 0) {\n            size++;\n            val = Math.floor(val / 128);\n        }\n    }\n\n    const dst = new Uint8Array(size);\n    const offset = new IntWrapper(0);\n\n    for (let i = 0; i < values.length; i++) {\n        encodeVarintFloat64Value(values[i], dst, offset);\n    }\n\n    return dst;\n}\n\n/**\n * Encodes a single number into the buffer at the given offset using Varint encoding.\n * Handles numbers up to 2^53 (MAX_SAFE_INTEGER) correctly.\n */\nfunction encodeVarintFloat64Value(val: number, buf: Uint8Array, offset: IntWrapper): void {\n    // Ensure integer\n    val = Math.floor(val);\n\n    // Handle 0 explicitly or ensure loop runs once\n    if (val === 0) {\n        buf[offset.get()] = 0;\n        offset.increment();\n        return;\n    }\n\n    while (val >= 128) {\n        // Write 7 bits of data | 0x80 (continuation bit)\n        buf[offset.get()] = (val % 128) | 0x80;\n        offset.increment();\n        // Shift right by 7 bits\n        val = Math.floor(val / 128);\n    }\n\n    // Write the last byte (no continuation bit)\n    buf[offset.get()] = val;\n    offset.increment();\n}\n\nexport function encodeFastPfor(values: Uint32Array): Uint8Array {\n    const encoderWorkspace = createFastPforEncoderWorkspace();\n    const encodedWords = encodeFastPforInt32WithWorkspace(values, encoderWorkspace);\n    return encodeBigEndianInt32s(encodedWords);\n}\n\nexport function encodeZigZagInt32Value(value: number): number {\n    return (value << 1) ^ (value >> 31);\n}\n\nexport function encodeZigZagInt64Value(value: bigint): bigint {\n    return (value << 1n) ^ (value >> 63n);\n}\n\nexport function encodeZigZagFloat64Value(n: number): number {\n    return n >= 0 ? n * 2 : n * -2 - 1;\n}\nexport function encodeZigZagInt32(data: Int32Array): Uint32Array {\n    const result = new Uint32Array(data.length);\n    for (let i = 0; i < data.length; i++) {\n        result[i] = encodeZigZagInt32Value(data[i]);\n    }\n    return result;\n}\n\nexport function encodeZigZagInt64(data: BigInt64Array): BigUint64Array {\n    const result = new BigUint64Array(data.length);\n    for (let i = 0; i < data.length; i++) {\n        result[i] = encodeZigZagInt64Value(data[i]);\n    }\n    return result;\n}\n\nexport function encodeZigZagFloat64(data: Float64Array): void {\n    for (let i = 0; i < data.length; i++) {\n        data[i] = encodeZigZagFloat64Value(data[i]);\n    }\n}\n\nexport function encodeUnsignedRleInt32(input: Uint32Array): { data: Uint32Array; runs: number } {\n    if (input.length === 0) {\n        return { data: new Uint32Array(0), runs: 0 };\n    }\n\n    const runLengths: number[] = [];\n    const runValues: number[] = [];\n\n    let currentRunLength = 0;\n    let currentValue = input[0];\n\n    for (let i = 0; i < input.length; i++) {\n        const nextValue = input[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            // End of the current run, record it\n            runLengths.push(currentRunLength);\n            runValues.push(currentValue);\n\n            // Start a new run\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n\n    // Record the final run after the loop finishes\n    runLengths.push(currentRunLength);\n    runValues.push(currentValue);\n\n    // Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    const encodedData = new Uint32Array(numRuns * 2);\n\n    // Populate the first half with lengths\n    encodedData.set(runLengths, 0);\n\n    // Populate the second half with values, offset by the total number of runs\n    encodedData.set(runValues, numRuns);\n\n    return { data: encodedData, runs: numRuns };\n}\n\nexport function encodeUnsignedRleInt64(input: BigInt64Array): { data: BigUint64Array; runs: number } {\n    if (input.length === 0) {\n        return { data: new BigUint64Array(0), runs: 0 };\n    }\n\n    const runLengths: number[] = [];\n    const runValues: bigint[] = [];\n\n    let currentRunLength = 0;\n    let currentValue: bigint = input[0];\n\n    for (let i = 0; i < input.length; i++) {\n        const nextValue = input[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            // End of the current run, record it\n            runLengths.push(currentRunLength);\n            runValues.push(currentValue);\n\n            // Start a new run\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n\n    // Record the final run after the loop finishes\n    runLengths.push(currentRunLength);\n    runValues.push(currentValue);\n\n    // Combine lengths and values into the final structured output array (BigUint64Array)\n    const numRuns = runLengths.length;\n    const encodedData = new BigUint64Array(numRuns * 2);\n\n    // Populate the first half with lengths, converting the run length numbers to bigint for storage in the BigUint64Array.\n    for (let i = 0; i < numRuns; i++) {\n        encodedData[i] = BigInt(runLengths[i]);\n    }\n\n    // Populate the second half with values, offset by the total number of runs\n    encodedData.set(runValues, numRuns);\n\n    return { data: encodedData, runs: numRuns };\n}\n\nexport function encodeUnsignedRleFloat64(input: Float64Array): { data: Float64Array; runs: number } {\n    if (input.length === 0) {\n        return { data: new Float64Array(0), runs: 0 };\n    }\n\n    const runLengths: number[] = [];\n    const runValues: number[] = [];\n\n    let currentRunLength = 0;\n    let currentValue = input[0];\n\n    for (let i = 0; i < input.length; i++) {\n        const nextValue = input[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            // End of the current run, record it\n            runLengths.push(currentRunLength);\n            runValues.push(currentValue);\n\n            // Start a new run\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n\n    // Record the final run after the loop finishes\n    runLengths.push(currentRunLength);\n    runValues.push(currentValue);\n\n    // Combine lengths and values into the final structured output array (Float64Array)\n    const numRuns = runLengths.length;\n    // The final array is twice the size of the number of runs\n    const encodedData = new Float64Array(numRuns * 2);\n\n    // Populate the first half with lengths\n    encodedData.set(runLengths, 0);\n\n    // Populate the second half with values, offset by the total number of runs\n    encodedData.set(runValues, numRuns);\n\n    return { data: encodedData, runs: numRuns };\n}\n\nexport function encodeZigZagDeltaInt32(data: Int32Array): Uint32Array {\n    if (data.length === 0) {\n        return new Uint32Array(0);\n    }\n\n    const encodedData = new Uint32Array(data.length);\n    let previousValue = data[0];\n    encodedData[0] = encodeZigZagInt32Value(previousValue);\n\n    for (let i = 1; i < data.length; i++) {\n        const currentValue = data[i];\n        const delta = currentValue - previousValue;\n        const encodedDelta = encodeZigZagInt32Value(delta);\n\n        // Store the encoded delta back into the array\n        encodedData[i] = encodedDelta;\n\n        // Update the previous value tracker for the next iteration's delta calculation\n        previousValue = currentValue;\n    }\n    return encodedData;\n}\n\nexport function encodeZigZagDeltaInt64(data: BigInt64Array): BigUint64Array {\n    if (data.length === 0) {\n        return new BigUint64Array(0);\n    }\n\n    const encodedData = new BigUint64Array(data.length);\n    let previousValue = data[0];\n    encodedData[0] = encodeZigZagInt64Value(previousValue);\n\n    for (let i = 1; i < data.length; i++) {\n        const currentValue = data[i];\n        const delta = currentValue - previousValue;\n        const encodedDelta = encodeZigZagInt64Value(delta);\n\n        // Store the encoded delta back into the array\n        encodedData[i] = encodedDelta;\n\n        // Update the previous value tracker for the next iteration's delta calculation\n        previousValue = currentValue;\n    }\n    return encodedData;\n}\n\nexport function encodeZigZagDeltaFloat64(data: Float64Array): void {\n    if (data.length === 0) {\n        return;\n    }\n\n    let previousValue = data[0];\n    data[0] = encodeZigZagFloat64Value(previousValue);\n\n    for (let i = 1; i < data.length; i++) {\n        const currentValue = data[i];\n        const delta = currentValue - previousValue;\n        const encodedDelta = encodeZigZagFloat64Value(delta);\n\n        // Store the encoded delta back into the array\n        data[i] = encodedDelta;\n\n        // Update the previous value tracker for the next iteration's delta calculation\n        previousValue = currentValue;\n    }\n}\n\nexport function encodeZigZagRleInt32(input: Int32Array): {\n    data: Uint32Array;\n    runs: number;\n    numTotalValues: number;\n} {\n    if (input.length === 0) {\n        return { data: new Uint32Array(0), runs: 0, numTotalValues: 0 };\n    }\n\n    const zigzagEncodedStream: number[] = [];\n\n    // Step 1: Apply Zigzag Encoding to all values\n    for (let i = 0; i < input.length; i++) {\n        zigzagEncodedStream.push(encodeZigZagInt32Value(input[i]));\n    }\n    // zigzagEncodedStream now holds the intermediate stream of zigzag values\n\n    // Step 2: Apply RLE to the stream of zigzag-encoded values\n    const runLengths: number[] = [];\n    const runZigZagValues: number[] = [];\n\n    let currentRunLength = 0;\n    let currentValue = zigzagEncodedStream[0];\n\n    for (let i = 0; i < zigzagEncodedStream.length; i++) {\n        const nextValue = zigzagEncodedStream[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            runLengths.push(currentRunLength);\n            runZigZagValues.push(currentValue);\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n    // Record the final run\n    runLengths.push(currentRunLength);\n    runZigZagValues.push(currentValue);\n\n    // Step 3: Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    // The final array uses Uint32Array for lengths AND values\n    const encodedData = new Uint32Array(numRuns * 2);\n\n    // Populate the first half with lengths\n    encodedData.set(runLengths, 0);\n\n    // Populate the second half with zigzagged values\n    encodedData.set(runZigZagValues, numRuns);\n\n    return {\n        data: encodedData,\n        runs: numRuns,\n        numTotalValues: input.length, // Total original values count\n    };\n}\n\nexport function encodeZigZagRleInt64(input: BigInt64Array): {\n    data: BigUint64Array;\n    runs: number;\n    numTotalValues: number;\n} {\n    if (input.length === 0) {\n        return { data: new BigUint64Array(0), runs: 0, numTotalValues: 0 };\n    }\n\n    const zigzagEncodedStream: bigint[] = [];\n\n    // Step 1: Apply Zigzag Encoding to all values\n    for (let i = 0; i < input.length; i++) {\n        zigzagEncodedStream.push(encodeZigZagInt64Value(input[i]));\n    }\n    // zigzagEncodedStream now holds the intermediate stream of zigzag values\n\n    // Step 2: Apply RLE to the stream of zigzag-encoded values\n    const runLengths: number[] = [];\n    const runZigZagValues: bigint[] = [];\n\n    let currentRunLength = 0;\n    let currentValue: bigint = zigzagEncodedStream[0];\n\n    for (let i = 0; i < zigzagEncodedStream.length; i++) {\n        const nextValue = zigzagEncodedStream[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            runLengths.push(currentRunLength);\n            runZigZagValues.push(currentValue);\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n    // Record the final run\n    runLengths.push(currentRunLength);\n    runZigZagValues.push(currentValue);\n\n    // Step 3: Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    // The final array uses BigUint64Array for lengths AND values\n    const encodedData = new BigUint64Array(numRuns * 2);\n\n    // Populate the first half with lengths (converting numbers back to BigUint64Array format)\n    for (let i = 0; i < numRuns; i++) {\n        encodedData[i] = BigInt(runLengths[i]);\n    }\n\n    // Populate the second half with zigzagged values\n    encodedData.set(runZigZagValues, numRuns);\n\n    return {\n        data: encodedData,\n        runs: numRuns,\n        numTotalValues: input.length, // Total original values count\n    };\n}\n\nexport function encodeZigZagRleFloat64(input: Float64Array): {\n    data: Float64Array;\n    runs: number;\n    numTotalValues: number;\n} {\n    if (input.length === 0) {\n        return { data: new Float64Array(0), runs: 0, numTotalValues: 0 };\n    }\n\n    const zigzagEncodedStream: number[] = [];\n\n    // Step 1: Apply Float-based Zigzag Encoding to all values\n    for (let i = 0; i < input.length; i++) {\n        zigzagEncodedStream.push(encodeZigZagFloat64Value(input[i]));\n    }\n    // zigzagEncodedStream now holds the intermediate stream of zigzag values (as floats acting as integers)\n\n    // Step 2: Apply RLE to the stream of zigzag-encoded values\n    const runLengths: number[] = [];\n    const runZigZagValues: number[] = [];\n\n    let currentRunLength = 0;\n    let currentValue = zigzagEncodedStream[0];\n\n    for (let i = 0; i < zigzagEncodedStream.length; i++) {\n        const nextValue = zigzagEncodedStream[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            runLengths.push(currentRunLength);\n            runZigZagValues.push(currentValue);\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n    // Record the final run\n    runLengths.push(currentRunLength);\n    runZigZagValues.push(currentValue);\n\n    // Step 3: Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    // The final array uses Float64Array for lengths AND values\n    const encodedData = new Float64Array(numRuns * 2);\n\n    // Populate the first half with lengths\n    encodedData.set(runLengths, 0);\n\n    // Populate the second half with zigzagged values\n    encodedData.set(runZigZagValues, numRuns);\n\n    return {\n        data: encodedData,\n        runs: numRuns,\n        numTotalValues: input.length, // Total original values count\n    };\n}\n\n/**\n * This is not really a encode, but more of a decode method...\n */\nexport function encodeDeltaInt32(data: Int32Array | Uint32Array): void {\n    if (data.length === 0) {\n        return;\n    }\n    for (let i = data.length - 1; i >= 1; i--) {\n        data[i] = data[i] - data[i - 1];\n    }\n}\n\nexport function encodeComponentwiseDeltaVec2(data: Int32Array): Uint32Array {\n    if (data.length < 2) return new Uint32Array(data);\n\n    const encoded = new Uint32Array(data.length);\n\n    // Reverse iterate to avoid overwriting data needed for delta computation\n    for (let i = data.length - 2; i >= 2; i -= 2) {\n        const deltaX = data[i] - data[i - 2];\n        const deltaY = data[i + 1] - data[i - 1];\n        encoded[i] = encodeZigZagInt32Value(deltaX);\n        encoded[i + 1] = encodeZigZagInt32Value(deltaY);\n    }\n\n    // Encode first vertex last (after computing all deltas)\n    encoded[0] = encodeZigZagInt32Value(data[0]);\n    encoded[1] = encodeZigZagInt32Value(data[1]);\n    return encoded;\n}\n\nexport function encodeComponentwiseDeltaVec2Scaled(data: Int32Array, scale: number): Uint32Array {\n    if (data.length < 2) return new Uint32Array(data);\n    const encoded = new Uint32Array(data.length);\n\n    // First, inverse scale all values (tile space -> original space)\n    for (let i = 0; i < data.length; i++) {\n        encoded[i] = Math.round(data[i] / scale);\n    }\n\n    // Then apply componentwise delta encoding (same as non-scaled version)\n    // Reverse iterate to avoid overwriting data needed for delta computation\n    for (let i = encoded.length - 2; i >= 2; i -= 2) {\n        const deltaX = encoded[i] - encoded[i - 2];\n        const deltaY = encoded[i + 1] - encoded[i - 1];\n        encoded[i] = encodeZigZagInt32Value(deltaX);\n        encoded[i + 1] = encodeZigZagInt32Value(deltaY);\n    }\n\n    // Encode first vertex last (after computing all deltas)\n    encoded[0] = encodeZigZagInt32Value(encoded[0]);\n    encoded[1] = encodeZigZagInt32Value(encoded[1]);\n    return encoded;\n}\n\n// HM TODO:\n// zigZagDeltaOfDeltaDecoding\n\nexport function encodeZigZagRleDeltaInt32(values: Int32Array | number[]): {\n    data: Uint32Array;\n    runs: number;\n    numTotalValues: number;\n} {\n    if (values.length === 0) {\n        return { data: new Uint32Array(0), runs: 0, numTotalValues: 0 };\n    }\n\n    const runLengths: number[] = [];\n    const encodedDeltas: number[] = [];\n\n    // The decoder explicitly sets decodedValues[0] = 0 and uses previousValue = 0.\n    // Therefore, we initialize our 'previous' tracker to 0 to calculate the first delta correctly.\n    let previousValue = 0;\n\n    // Variables to track the current run\n    let currentDelta: number | null = null;\n    let currentRunLength = 0;\n\n    for (let i = 0; i < values.length; i++) {\n        const value = values[i];\n        const delta = value - previousValue;\n        previousValue = value;\n\n        if (currentDelta === null) {\n            // First element initialization\n            currentDelta = delta;\n            currentRunLength = 1;\n        } else if (delta === currentDelta) {\n            // Continuation of the current run\n            currentRunLength++;\n        } else {\n            // The run has broken (delta changed)\n            // 1. Push the length of the previous run\n            runLengths.push(currentRunLength);\n            // 2. ZigZag encode the previous delta and push it\n            encodedDeltas.push(encodeZigZagInt32Value(currentDelta));\n\n            // Start the new run\n            currentDelta = delta;\n            currentRunLength = 1;\n        }\n    }\n\n    // Flush the final run remaining after the loop finishes\n    if (currentDelta !== null) {\n        runLengths.push(currentRunLength);\n        encodedDeltas.push(encodeZigZagInt32Value(currentDelta));\n    }\n\n    const numRuns = runLengths.length;\n\n    // The decoder expects 'data' to be: [RunLength 1, RunLength 2... | Value 1, Value 2...]\n    // Size is numRuns * 2 (First half lengths, second half values)\n    const data = new Uint32Array(numRuns * 2);\n\n    for (let i = 0; i < numRuns; i++) {\n        data[i] = runLengths[i]; // First half: Run Lengths\n        data[i + numRuns] = encodedDeltas[i]; // Second half: ZigZag Encoded Deltas\n    }\n\n    return {\n        data: data,\n        runs: numRuns,\n        numTotalValues: values.length,\n    };\n}\n\nexport function encodeRleDeltaInt32(values: Uint32Array | number[]): {\n    data: Uint32Array;\n    runs: number;\n    numTotalValues: number;\n} {\n    if (values.length === 0) {\n        return { data: new Uint32Array(0), runs: 0, numTotalValues: 0 };\n    }\n\n    const runLengths: number[] = [];\n    const deltas: number[] = [];\n\n    // The decoder logic relies on: decodedValues[0] = 0; previousValue = 0;\n    // So the encoder must assume the sequence starts relative to 0.\n    let previousValue = 0;\n\n    // Track the current run of deltas\n    let currentDelta: number | null = null;\n    let currentRunLength = 0;\n\n    for (let i = 0; i < values.length; i++) {\n        const value = values[i];\n        const delta = value - previousValue;\n        previousValue = value;\n\n        if (currentDelta === null) {\n            // Initialize first run\n            currentDelta = delta;\n            currentRunLength = 1;\n        } else if (delta === currentDelta) {\n            // Continue current run\n            currentRunLength++;\n        } else {\n            // Delta changed: flush the previous run\n            runLengths.push(currentRunLength);\n            deltas.push(currentDelta);\n\n            // Start new run\n            currentDelta = delta;\n            currentRunLength = 1;\n        }\n    }\n\n    // Flush the final run\n    if (currentDelta !== null) {\n        runLengths.push(currentRunLength);\n        deltas.push(currentDelta);\n    }\n\n    const numRuns = runLengths.length;\n\n    // Pack into Uint32Array: [ RunLength 1...N | Delta 1...N ]\n    const data = new Uint32Array(numRuns * 2);\n    for (let i = 0; i < numRuns; i++) {\n        data[i] = runLengths[i];\n        data[i + numRuns] = deltas[i];\n    }\n\n    return {\n        data: data,\n        runs: numRuns,\n        numTotalValues: values.length,\n    };\n}\n\nexport function encodeDeltaRleInt32(input: Int32Array): {\n    data: Uint32Array;\n    runs: number;\n    numValues: number;\n} {\n    if (input.length === 0) {\n        return { data: new Uint32Array(0), runs: 0, numValues: 0 };\n    }\n\n    const deltasAndEncoded: number[] = [];\n    let previousValue = 0;\n\n    // Step 1 & 2: Calculate Deltas and Zigzag Encode them\n    for (let i = 0; i < input.length; i++) {\n        const currentValue = input[i];\n        const delta = currentValue - previousValue;\n        const encodedDelta = encodeZigZagInt32Value(delta);\n        deltasAndEncoded.push(encodedDelta);\n        previousValue = currentValue;\n    }\n    // deltasAndEncoded now holds the intermediate stream of zigzagged deltas\n\n    // Step 3: Apply RLE to the stream of zigzag-encoded deltas\n    const runLengths: number[] = [];\n    const runZigZagDeltas: number[] = [];\n\n    let currentRunLength = 0;\n    let currentRunValue = deltasAndEncoded[0];\n\n    for (let i = 0; i < deltasAndEncoded.length; i++) {\n        const nextValue = deltasAndEncoded[i];\n\n        if (nextValue === currentRunValue) {\n            currentRunLength++;\n        } else {\n            runLengths.push(currentRunLength);\n            runZigZagDeltas.push(currentRunValue);\n            currentRunValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n    // Record the final run\n    runLengths.push(currentRunLength);\n    runZigZagDeltas.push(currentRunValue);\n\n    // Step 4: Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    const encodedData = new Uint32Array(numRuns * 2);\n\n    // Populate the first half with lengths\n    for (let i = 0; i < numRuns; i++) {\n        encodedData[i] = runLengths[i];\n    }\n\n    // Populate the second half with zigzagged deltas\n    // Uint32Array.set() works with standard number arrays\n    encodedData.set(runZigZagDeltas, numRuns);\n\n    return {\n        data: encodedData,\n        runs: numRuns,\n        numValues: input.length, // Total original values count\n    };\n}\n\nexport function encodeDeltaRleInt64(input: BigInt64Array): {\n    data: BigUint64Array;\n    runs: number;\n    numValues: number;\n} {\n    if (input.length === 0) {\n        return { data: new BigUint64Array(0), runs: 0, numValues: 0 };\n    }\n\n    const deltasAndEncoded: bigint[] = [];\n    let previousValue = 0n;\n\n    // Step 1 & 2: Calculate Deltas and Zigzag Encode them\n    for (let i = 0; i < input.length; i++) {\n        const currentValue = input[i];\n        const delta = currentValue - previousValue;\n        const encodedDelta = encodeZigZagInt64Value(delta);\n        deltasAndEncoded.push(encodedDelta);\n        previousValue = currentValue;\n    }\n    // deltasAndEncoded now holds the intermediate stream of zigzagged deltas\n\n    // Step 3: Apply RLE to the stream of zigzag-encoded deltas\n    const runLengths: number[] = [];\n    const runZigZagDeltas: bigint[] = [];\n\n    let currentRunLength = 0;\n    let currentValue = deltasAndEncoded[0];\n\n    for (let i = 0; i < deltasAndEncoded.length; i++) {\n        const nextValue = deltasAndEncoded[i];\n\n        if (nextValue === currentValue) {\n            currentRunLength++;\n        } else {\n            runLengths.push(currentRunLength);\n            runZigZagDeltas.push(currentValue);\n            currentValue = nextValue;\n            currentRunLength = 1;\n        }\n    }\n    // Record the final run\n    runLengths.push(currentRunLength);\n    runZigZagDeltas.push(currentValue);\n\n    // Step 4: Combine lengths and values into the final structured output array\n    const numRuns = runLengths.length;\n    const encodedData = new BigUint64Array(numRuns * 2);\n\n    // Populate the first half with lengths (converting numbers back to BigUint64Array for storage)\n    for (let i = 0; i < numRuns; i++) {\n        encodedData[i] = BigInt(runLengths[i]);\n    }\n\n    // Populate the second half with zigzagged deltas\n    encodedData.set(runZigZagDeltas, numRuns);\n\n    return {\n        data: encodedData,\n        runs: numRuns,\n        numValues: input.length, // Total original values count\n    };\n}\n"
  },
  {
    "path": "ts/src/encoding/integerStreamEncoder.ts",
    "content": "import type { StreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport {\n    encodeDeltaRleInt32,\n    encodeZigZagInt32,\n    encodeZigZagRleInt32,\n    encodeUnsignedRleInt32,\n    encodeDeltaInt32,\n    encodeUnsignedRleFloat64,\n    encodeZigZagDeltaFloat64,\n    encodeZigZagFloat64,\n    encodeZigZagRleFloat64,\n    encodeVarintInt32,\n    encodeVarintInt64,\n    encodeZigZagInt64Value,\n    encodeFastPfor,\n    encodeComponentwiseDeltaVec2,\n    encodeComponentwiseDeltaVec2Scaled,\n    encodeZigZagDeltaInt32,\n} from \"./integerEncodingUtils\";\nimport type BitVector from \"../vector/flat/bitVector\";\nimport { packNullable } from \"./packNullableUtils\";\nimport { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport type GeometryScaling from \"../decoding/geometryScaling\";\n\nexport function encodeSignedInt32Stream(\n    values: Int32Array,\n    metadata: StreamMetadata,\n    bitVector?: BitVector,\n    scalingData?: GeometryScaling,\n): Uint8Array {\n    const { data } = encodeSignedInt32(values, metadata, bitVector, scalingData);\n    return encodePhysicalLevelTechnique(data, metadata);\n}\n\nexport function encodeUnsignedInt32Stream(\n    values: Uint32Array,\n    metadata: StreamMetadata,\n    bitVector?: BitVector,\n    scalingData?: GeometryScaling,\n): Uint8Array {\n    const { data } = encodeUnsignedInt32(values, metadata, bitVector, scalingData);\n    return encodePhysicalLevelTechnique(data, metadata);\n}\n\nfunction encodePhysicalLevelTechnique(data: Uint32Array, streamMetadata: StreamMetadata): Uint8Array {\n    const physicalLevelTechnique = streamMetadata.physicalLevelTechnique;\n    if (physicalLevelTechnique === PhysicalLevelTechnique.FAST_PFOR) {\n        return encodeFastPfor(data);\n    }\n    if (physicalLevelTechnique === PhysicalLevelTechnique.VARINT) {\n        return encodeVarintInt32(data);\n    }\n\n    if (physicalLevelTechnique === PhysicalLevelTechnique.NONE) {\n        const slice = data.subarray(0, streamMetadata.byteLength);\n        return new Uint8Array(slice);\n    }\n\n    throw new Error(\"Specified physicalLevelTechnique is not supported (yet).\");\n}\n\nfunction encodeSignedInt32(\n    values: Int32Array,\n    streamMetadata: StreamMetadata,\n    bitVector?: BitVector,\n    scalingData?: GeometryScaling,\n): { data: Uint32Array; runs?: number } {\n    values = bitVector ? packNullable(values, bitVector) : new Int32Array(values);\n    let data: Uint32Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const encoded = encodeDeltaRleInt32(values);\n                return { data: encoded.data, runs: encoded.runs };\n            } else {\n                data = encodeZigZagDeltaInt32(values);\n                return { data };\n            }\n        case LogicalLevelTechnique.RLE: {\n            const encoded = encodeZigZagRleInt32(values);\n            return { data: encoded.data, runs: encoded.runs };\n        }\n        case LogicalLevelTechnique.MORTON:\n            encodeDeltaInt32(values);\n            data = new Uint32Array(values);\n            return { data };\n        case LogicalLevelTechnique.COMPONENTWISE_DELTA:\n            if (scalingData && !bitVector) {\n                const data = encodeComponentwiseDeltaVec2Scaled(values, scalingData.scale);\n                return { data };\n            }\n            data = encodeComponentwiseDeltaVec2(values);\n            return { data };\n        case LogicalLevelTechnique.NONE:\n            data = encodeZigZagInt32(values);\n            return { data };\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n}\n\nfunction encodeUnsignedInt32(\n    values: Uint32Array,\n    streamMetadata: StreamMetadata,\n    bitVector?: BitVector,\n    scalingData?: GeometryScaling,\n): { data: Uint32Array; runs?: number } {\n    values = bitVector ? packNullable(values, bitVector) : new Uint32Array(values);\n    let data: Uint32Array;\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                const encoded = encodeDeltaRleInt32(new Int32Array(values.buffer, values.byteOffset, values.length));\n                return { data: encoded.data, runs: encoded.runs };\n            }\n            data = encodeZigZagDeltaInt32(new Int32Array(values.buffer, values.byteOffset, values.length));\n            return { data };\n        case LogicalLevelTechnique.RLE: {\n            const encoded = encodeUnsignedRleInt32(values);\n            return { data: encoded.data, runs: encoded.runs };\n        }\n        case LogicalLevelTechnique.MORTON:\n            encodeDeltaInt32(values);\n            data = values;\n            return { data };\n        case LogicalLevelTechnique.COMPONENTWISE_DELTA:\n            if (scalingData && !bitVector) {\n                const data = encodeComponentwiseDeltaVec2Scaled(new Int32Array(values), scalingData.scale);\n                return { data };\n            }\n            data = encodeComponentwiseDeltaVec2(new Int32Array(values));\n            return { data };\n        case LogicalLevelTechnique.NONE:\n            data = values;\n            return { data };\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n}\n\nexport function encodeFloat64(values: Float64Array, streamMetadata: StreamMetadata, isSigned: boolean): Float64Array {\n    switch (streamMetadata.logicalLevelTechnique1) {\n        case LogicalLevelTechnique.DELTA:\n            encodeZigZagDeltaFloat64(values);\n            if (streamMetadata.logicalLevelTechnique2 === LogicalLevelTechnique.RLE) {\n                values = encodeUnsignedRleFloat64(values).data;\n            }\n            return values;\n        case LogicalLevelTechnique.RLE:\n            return encodeRleFloat64(values, isSigned);\n        case LogicalLevelTechnique.NONE:\n            if (isSigned) {\n                encodeZigZagFloat64(values);\n            }\n            return values;\n        default:\n            throw new Error(\n                `The specified Logical level technique is not supported: ${streamMetadata.logicalLevelTechnique1}`,\n            );\n    }\n}\n\nfunction encodeRleFloat64(data: Float64Array, isSigned: boolean): Float64Array {\n    return isSigned ? encodeZigZagRleFloat64(data).data : encodeUnsignedRleFloat64(data).data;\n}\n\n/**\n * Encodes BigInt64 values with zigzag encoding and varint compression\n */\nexport function encodeInt64SignedNone(values: BigInt64Array): Uint8Array {\n    const zigzagEncoded = new BigUint64Array(Array.from(values, (val) => encodeZigZagInt64Value(val)));\n    return encodeVarintInt64(zigzagEncoded);\n}\n\n/**\n * Encodes BigInt64 values with delta encoding, zigzag, and varint\n */\nexport function encodeInt64SignedDelta(values: BigInt64Array): Uint8Array {\n    const deltaEncoded = new BigInt64Array(values.length);\n    deltaEncoded[0] = values[0];\n    for (let i = 1; i < values.length; i++) {\n        deltaEncoded[i] = values[i] - values[i - 1];\n    }\n    const zigzagEncoded = new BigUint64Array(deltaEncoded.length);\n    for (let i = 0; i < deltaEncoded.length; i++) {\n        zigzagEncoded[i] = encodeZigZagInt64Value(deltaEncoded[i]);\n    }\n    return encodeVarintInt64(zigzagEncoded);\n}\n\n/**\n * Encodes BigInt64 values with RLE, zigzag, and varint\n * @param runs - Array of [runLength, value] pairs\n */\nexport function encodeInt64SignedRle(runs: Array<[number, bigint]>): Uint8Array {\n    const runLengths: bigint[] = [];\n    const values: bigint[] = [];\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(BigInt(runLength));\n        values.push(encodeZigZagInt64Value(value));\n    }\n\n    const rleValues = [...runLengths, ...values];\n    return encodeVarintInt64(new BigUint64Array(rleValues));\n}\n\n/**\n * Encodes BigInt64 values with delta+RLE, zigzag, and varint\n * @param runs - Array of [runLength, deltaValue] pairs representing RLE-encoded delta values\n */\nexport function encodeInt64SignedDeltaRle(runs: Array<[number, bigint]>): Uint8Array {\n    const runLengths: bigint[] = [];\n    const values: bigint[] = [];\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(BigInt(runLength));\n        values.push(encodeZigZagInt64Value(value));\n    }\n\n    const rleValues = [...runLengths, ...values];\n    return encodeVarintInt64(new BigUint64Array(rleValues));\n}\n\n/**\n * Encodes unsigned BigInt64 values with varint compression (no zigzag)\n */\nexport function encodeInt64UnsignedNone(values: BigInt64Array): Uint8Array {\n    return encodeVarintInt64(new BigUint64Array(values));\n}\n"
  },
  {
    "path": "ts/src/encoding/packNullableUtils.ts",
    "content": "import type { TypedArrayConstructor, TypedArrayInstance } from \"../decoding/unpackNullableUtils\";\nimport BitVector from \"../vector/flat/bitVector\";\n\nexport function packNullable<T extends TypedArrayInstance>(data: T, presentBits: BitVector | null): T {\n    // Non-nullable case: if no mask is provided, the data is already \"packed\"\n    if (!presentBits) {\n        return data;\n    }\n\n    const size = data.length;\n\n    // 1. First pass: Count how many elements are actually present\n    // This is required to allocate the correct size for the TypedArray\n    let packedCount = 0;\n    for (let i = 0; i < size; i++) {\n        if (presentBits.get(i)) {\n            packedCount++;\n        }\n    }\n\n    // 2. Create a new array of the same type with the reduced size\n    const constructor = data.constructor as TypedArrayConstructor;\n    const result = new constructor(packedCount) as T;\n\n    // 3. Second pass: Fill the result array with valid values\n    let counter = 0;\n    for (let i = 0; i < size; i++) {\n        if (presentBits.get(i)) {\n            result[counter++] = data[i];\n        }\n    }\n\n    return result;\n}\n\nexport function packNullableBoolean(data: Uint8Array, dataSize: number, presentBits: BitVector | null): Uint8Array {\n    // Non-nullable case: if no mask is provided, the data is already \"packed\"\n    if (!presentBits) {\n        return data;\n    }\n\n    const inputBitVector = new BitVector(data, dataSize);\n\n    // 1. Calculate how many bits are actually marked as 'present'\n    // This determines the size of the final packed buffer.\n    let packedCount = 0;\n    for (let i = 0; i < dataSize; i++) {\n        if (presentBits.get(i)) {\n            packedCount++;\n        }\n    }\n\n    // 2. Initialize the result BitVector with the correct compressed size\n    const resultBuffer = new Uint8Array(Math.ceil(packedCount / 8));\n    const resultBitVector = new BitVector(resultBuffer, packedCount);\n\n    // 3. Fill the result: only copy bits where the mask is true\n    let targetIndex = 0;\n    for (let i = 0; i < dataSize; i++) {\n        if (presentBits.get(i)) {\n            const value = inputBitVector.get(i);\n            resultBitVector.set(targetIndex++, value);\n        }\n    }\n\n    return resultBitVector.getBuffer();\n}\n"
  },
  {
    "path": "ts/src/encoding/propertyEncoder.ts",
    "content": "import { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { DictionaryType } from \"../metadata/tile/dictionaryType\";\nimport type { StreamMetadata, RleEncodedStreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport IntWrapper from \"../decoding/intWrapper\";\nimport { encodeBooleanRle, encodeFloatsLE, encodeDoubleLE } from \"./encodingUtils\";\nimport {\n    encodeVarintInt32Value,\n    encodeVarintInt32,\n    encodeVarintInt64,\n    encodeZigZagInt32Value,\n    encodeZigZagInt64Value,\n    encodeZigZagInt32,\n} from \"./integerEncodingUtils\";\n\n/**\n * Encodes INT_32 values with NONE encoding (no delta, no RLE)\n */\nexport function encodeInt32NoneColumn(values: Int32Array): Uint8Array {\n    const zigzagEncoded = encodeZigZagInt32(values);\n    const encodedData = encodeVarintInt32(zigzagEncoded);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_32 values with DELTA encoding\n */\nexport function encodeInt32DeltaColumn(values: Int32Array): Uint8Array {\n    // Delta encode: store deltas\n    const deltaEncoded = new Int32Array(values.length);\n    deltaEncoded[0] = values[0];\n    for (let i = 1; i < values.length; i++) {\n        deltaEncoded[i] = values[i] - values[i - 1];\n    }\n\n    const zigzagEncoded = encodeZigZagInt32(deltaEncoded);\n    const encodedData = encodeVarintInt32(zigzagEncoded);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_32 values with RLE encoding\n * @param runs - Array of [runLength, value] pairs\n */\nexport function encodeInt32RleColumn(runs: Array<[number, number]>): Uint8Array {\n    const runLengths: number[] = [];\n    const values: number[] = [];\n    let totalValues = 0;\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(runLength);\n        values.push(encodeZigZagInt32Value(value));\n        totalValues += runLength;\n    }\n\n    const rleValues = [...runLengths, ...values];\n    const encodedData = encodeVarintInt32(new Uint32Array(rleValues));\n    const streamMetadata = createRleMetadata(\n        LogicalLevelTechnique.RLE,\n        LogicalLevelTechnique.NONE,\n        runs.length,\n        totalValues,\n    );\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_32 values with DELTA+RLE encoding\n * @param runs - Array of [runLength, deltaValue] pairs, where first value is the base\n */\nexport function encodeInt32DeltaRleColumn(runs: Array<[number, number]>): Uint8Array {\n    const runLengths: number[] = [];\n    const values: number[] = [];\n    let totalValues = 0;\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(runLength);\n        values.push(encodeZigZagInt32Value(value));\n        totalValues += runLength;\n    }\n\n    const rleValues = [...runLengths, ...values];\n    const encodedData = encodeVarintInt32(new Uint32Array(rleValues));\n    const streamMetadata = createRleMetadata(\n        LogicalLevelTechnique.DELTA,\n        LogicalLevelTechnique.RLE,\n        runs.length,\n        totalValues,\n    );\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable INT_32 values\n */\nexport function encodeInt32NullableColumn(values: (number | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is number => v !== null);\n    const zigzagEncoded = new Uint32Array(nonNullValues.map((v) => encodeZigZagInt32Value(v)));\n    const encodedData = encodeVarintInt32(zigzagEncoded);\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    // Nullability stream\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\n/**\n * Encodes UINT_32 values (no zigzag encoding)\n */\nexport function encodeUint32Column(values: Uint32Array): Uint8Array {\n    const encodedData = encodeVarintInt32(values);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_64 values with NONE encoding\n */\nexport function encodeInt64NoneColumn(values: BigInt64Array): Uint8Array {\n    const zigzagEncoded = new BigUint64Array(Array.from(values, (val) => encodeZigZagInt64Value(val)));\n    const encodedData = encodeVarintInt64(zigzagEncoded);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_64 values with DELTA encoding\n */\nexport function encodeInt64DeltaColumn(values: BigInt64Array): Uint8Array {\n    const deltaEncoded = new BigInt64Array(values.length);\n    deltaEncoded[0] = values[0];\n    for (let i = 1; i < values.length; i++) {\n        deltaEncoded[i] = values[i] - values[i - 1];\n    }\n\n    const zigzagEncoded = new BigUint64Array(deltaEncoded.length);\n    for (let i = 0; i < deltaEncoded.length; i++) {\n        zigzagEncoded[i] = encodeZigZagInt64Value(deltaEncoded[i]);\n    }\n    const encodedData = encodeVarintInt64(zigzagEncoded);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.DELTA, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_64 values with RLE encoding\n */\nexport function encodeInt64RleColumn(runs: Array<[number, bigint]>): Uint8Array {\n    const runLengths: bigint[] = [];\n    const values: bigint[] = [];\n    let totalValues = 0;\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(BigInt(runLength));\n        values.push(encodeZigZagInt64Value(value));\n        totalValues += runLength;\n    }\n\n    const rleValues = [...runLengths, ...values];\n    const encodedData = encodeVarintInt64(new BigUint64Array(rleValues));\n    const streamMetadata = createRleMetadata(\n        LogicalLevelTechnique.RLE,\n        LogicalLevelTechnique.NONE,\n        runs.length,\n        totalValues,\n    );\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes INT_64 values with DELTA+RLE encoding\n */\nexport function encodeInt64DeltaRleColumn(runs: Array<[number, bigint]>): Uint8Array {\n    const runLengths: bigint[] = [];\n    const values: bigint[] = [];\n    let totalValues = 0;\n\n    for (const [runLength, value] of runs) {\n        runLengths.push(BigInt(runLength));\n        values.push(encodeZigZagInt64Value(value));\n        totalValues += runLength;\n    }\n\n    const rleValues = [...runLengths, ...values];\n    const encodedData = encodeVarintInt64(new BigUint64Array(rleValues));\n    const streamMetadata = createRleMetadata(\n        LogicalLevelTechnique.DELTA,\n        LogicalLevelTechnique.RLE,\n        runs.length,\n        totalValues,\n    );\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable INT_64 values\n */\nexport function encodeInt64NullableColumn(values: (bigint | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is bigint => v !== null);\n    const zigzagEncoded = new BigUint64Array(Array.from(nonNullValues, (val) => encodeZigZagInt64Value(val)));\n    const encodedData = encodeVarintInt64(zigzagEncoded);\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\n/**\n * Encodes UINT_64 values (no zigzag encoding)\n */\nexport function encodeUint64Column(values: BigUint64Array): Uint8Array {\n    const encodedData = encodeVarintInt64(values);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable UINT_64 values\n */\nexport function encodeUint64NullableColumn(values: (bigint | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is bigint => v !== null);\n    const encodedData = encodeVarintInt64(new BigUint64Array(nonNullValues));\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\n/**\n * Encodes FLOAT values\n */\nexport function encodeFloatColumn(values: Float32Array): Uint8Array {\n    const encodedData = encodeFloatsLE(values);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable FLOAT values\n */\nexport function encodeFloatNullableColumn(values: (number | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is number => v !== null);\n    const encodedData = encodeFloatsLE(new Float32Array(nonNullValues));\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\n/**\n * Encodes DOUBLE values\n */\nexport function encodeDoubleColumn(values: Float64Array): Uint8Array {\n    const encodedData = encodeDoubleLE(values);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable DOUBLE values\n */\nexport function encodeDoubleNullableColumn(values: (number | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is number => v !== null);\n    const encodedData = encodeDoubleLE(new Float64Array(nonNullValues));\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\n/**\n * Encodes BOOLEAN values\n */\nexport function encodeBooleanColumn(values: boolean[]): Uint8Array {\n    const encodedData = encodeBooleanRle(values);\n    const streamMetadata = createStreamMetadata(LogicalLevelTechnique.NONE, LogicalLevelTechnique.NONE, values.length);\n    return buildEncodedStream(streamMetadata, encodedData);\n}\n\n/**\n * Encodes nullable BOOLEAN values\n */\nexport function encodeBooleanNullableColumn(values: (boolean | null)[]): Uint8Array {\n    const nonNullValues = values.filter((v): v is boolean => v !== null);\n    const encodedData = encodeBooleanRle(nonNullValues);\n    const dataStreamMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nonNullValues.length,\n    );\n    const dataStream = buildEncodedStream(dataStreamMetadata, encodedData);\n\n    const nullabilityValues = values.map((v) => v !== null);\n    const nullabilityEncoded = encodeBooleanRle(nullabilityValues);\n    const nullabilityMetadata = createStreamMetadata(\n        LogicalLevelTechnique.NONE,\n        LogicalLevelTechnique.NONE,\n        nullabilityValues.length,\n    );\n    const nullabilityStream = buildEncodedStream(nullabilityMetadata, nullabilityEncoded);\n\n    return concatenateBuffers(nullabilityStream, dataStream);\n}\n\nfunction createStreamMetadata(\n    logicalTechnique1: LogicalLevelTechnique,\n    logicalTechnique2: LogicalLevelTechnique = LogicalLevelTechnique.NONE,\n    numValues = 3,\n): StreamMetadata {\n    return {\n        physicalStreamType: PhysicalStreamType.DATA,\n        logicalStreamType: { dictionaryType: DictionaryType.NONE },\n        logicalLevelTechnique1: logicalTechnique1,\n        logicalLevelTechnique2: logicalTechnique2,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues,\n        byteLength: 10,\n        decompressedCount: numValues,\n    };\n}\n\nfunction createRleMetadata(\n    logicalTechnique1: LogicalLevelTechnique,\n    logicalTechnique2: LogicalLevelTechnique,\n    runs: number,\n    numRleValues: number,\n): RleEncodedStreamMetadata {\n    return {\n        physicalStreamType: PhysicalStreamType.DATA,\n        logicalStreamType: { dictionaryType: DictionaryType.NONE },\n        logicalLevelTechnique1: logicalTechnique1,\n        logicalLevelTechnique2: logicalTechnique2,\n        physicalLevelTechnique: PhysicalLevelTechnique.VARINT,\n        numValues: runs * 2,\n        byteLength: 10,\n        decompressedCount: numRleValues,\n        runs,\n        numRleValues,\n    };\n}\n\nfunction buildEncodedStream(\n    streamMetadata: StreamMetadata | RleEncodedStreamMetadata,\n    encodedData: Uint8Array,\n): Uint8Array {\n    const updatedMetadata = {\n        ...streamMetadata,\n        byteLength: encodedData.length,\n    };\n\n    const metadataBuffer = encodeStreamMetadata(updatedMetadata);\n    const result = new Uint8Array(metadataBuffer.length + encodedData.length);\n    result.set(metadataBuffer, 0);\n    result.set(encodedData, metadataBuffer.length);\n\n    return result;\n}\n\nfunction encodeStreamMetadata(metadata: StreamMetadata | RleEncodedStreamMetadata): Uint8Array {\n    const buffer = new Uint8Array(100);\n    let writeOffset = 0;\n\n    // Byte 1: Stream type\n    const physicalTypeIndex = Object.values(PhysicalStreamType).indexOf(metadata.physicalStreamType);\n    const lowerNibble = 0; // For DATA stream with NONE dictionary type\n    buffer[writeOffset++] = (physicalTypeIndex << 4) | lowerNibble;\n\n    // Byte 2: Encoding techniques\n    const llt1Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique1);\n    const llt2Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique2);\n    const pltIndex = Object.values(PhysicalLevelTechnique).indexOf(metadata.physicalLevelTechnique);\n    buffer[writeOffset++] = (llt1Index << 5) | (llt2Index << 2) | pltIndex;\n\n    // Variable-length fields\n    const offset = new IntWrapper(writeOffset);\n    encodeVarintInt32Value(metadata.numValues, buffer, offset);\n    encodeVarintInt32Value(metadata.byteLength, buffer, offset);\n\n    // RLE-specific fields\n    if (isRleMetadata(metadata)) {\n        encodeVarintInt32Value(metadata.runs, buffer, offset);\n        encodeVarintInt32Value(metadata.numRleValues, buffer, offset);\n    }\n\n    return buffer.slice(0, offset.get());\n}\n\nfunction isRleMetadata(metadata: StreamMetadata | RleEncodedStreamMetadata): metadata is RleEncodedStreamMetadata {\n    return \"runs\" in metadata && \"numRleValues\" in metadata;\n}\n\nfunction concatenateBuffers(...buffers: Uint8Array[]): Uint8Array {\n    const totalLength = buffers.reduce((sum, buf) => sum + buf.length, 0);\n    const result = new Uint8Array(totalLength);\n    let offset = 0;\n\n    for (const buffer of buffers) {\n        result.set(buffer, offset);\n        offset += buffer.length;\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "ts/src/encoding/stringEncoder.ts",
    "content": "import { PhysicalStreamType } from \"../metadata/tile/physicalStreamType\";\nimport { DictionaryType } from \"../metadata/tile/dictionaryType\";\nimport { LengthType } from \"../metadata/tile/lengthType\";\nimport { OffsetType } from \"../metadata/tile/offsetType\";\nimport { PhysicalLevelTechnique } from \"../metadata/tile/physicalLevelTechnique\";\nimport { LogicalLevelTechnique } from \"../metadata/tile/logicalLevelTechnique\";\nimport IntWrapper from \"../decoding/intWrapper\";\nimport { encodeBooleanRle, encodeStrings, createStringLengths, concatenateBuffers } from \"./encodingUtils\";\nimport { encodeVarintInt32Value, encodeVarintInt32 } from \"./integerEncodingUtils\";\nimport type { StreamMetadata } from \"../metadata/tile/streamMetadataDecoder\";\nimport type { LogicalStreamType } from \"../metadata/tile/logicalStreamType\";\n\n/**\n * Encodes plain strings into a complete stream with PRESENT (if needed), LENGTH, and DATA streams.\n * @param strings - Array of strings (can include null values)\n * @returns Encoded Uint8Array that can be passed to decodeString\n */\nexport function encodePlainStrings(strings: (string | null)[]): Uint8Array {\n    const hasNull = strings.some((s) => s === null);\n    const nonNullStrings = strings.filter((s): s is string => s !== null);\n    const stringBytes = encodeStrings(nonNullStrings);\n\n    const streams: Uint8Array[] = [];\n\n    // Add PRESENT stream if nulls exist\n    if (hasNull) {\n        const nullabilityValues = strings.map((s) => s !== null);\n        streams.push(\n            createStream(PhysicalStreamType.PRESENT, encodeBooleanRle(nullabilityValues), {\n                technique: PhysicalLevelTechnique.VARINT,\n                count: nullabilityValues.length,\n            }),\n        );\n    }\n\n    // Add LENGTH stream\n    const lengths = createStringLengths(nonNullStrings);\n    streams.push(\n        createStream(PhysicalStreamType.LENGTH, encodeVarintInt32(lengths), {\n            logical: { lengthType: LengthType.VAR_BINARY },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: lengths.length,\n        }),\n    );\n\n    // Add DATA stream\n    streams.push(\n        createStream(PhysicalStreamType.DATA, stringBytes, {\n            logical: { dictionaryType: DictionaryType.NONE },\n        }),\n    );\n\n    return concatenateBuffers(...streams);\n}\n\n/**\n * Encodes dictionary-compressed strings into a complete stream.\n * @param strings - Array of strings (can include null values)\n * @returns Encoded Uint8Array that can be passed to decodeString\n */\nexport function encodeDictionaryStrings(strings: (string | null)[]): Uint8Array {\n    const hasNull = strings.some((s) => s === null);\n    const nonNullStrings = strings.filter((s): s is string => s !== null);\n\n    // Create dictionary of unique strings\n    const uniqueStrings = Array.from(new Set(nonNullStrings));\n    const stringMap = new Map(uniqueStrings.map((s, i) => [s, i]));\n    const offsets = nonNullStrings.map((s) => {\n        const offset = stringMap.get(s);\n        if (offset === undefined) {\n            throw new Error(`String not found in dictionary: ${s}`);\n        }\n        return offset;\n    });\n\n    const stringBytes = encodeStrings(uniqueStrings);\n    const lengths = createStringLengths(uniqueStrings);\n\n    const streams: Uint8Array[] = [];\n\n    // Add PRESENT stream if nulls exist\n    if (hasNull) {\n        const nullabilityValues = strings.map((s) => s !== null);\n        streams.push(\n            createStream(PhysicalStreamType.PRESENT, encodeBooleanRle(nullabilityValues), {\n                technique: PhysicalLevelTechnique.VARINT,\n                count: nullabilityValues.length,\n            }),\n        );\n    }\n\n    // Add OFFSET stream\n    streams.push(\n        createStream(PhysicalStreamType.OFFSET, encodeVarintInt32(new Uint32Array(offsets)), {\n            logical: { offsetType: OffsetType.STRING },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: offsets.length,\n        }),\n    );\n\n    // Add LENGTH stream (for dictionary)\n    streams.push(\n        createStream(PhysicalStreamType.LENGTH, encodeVarintInt32(lengths), {\n            logical: { lengthType: LengthType.DICTIONARY },\n            technique: PhysicalLevelTechnique.VARINT,\n            count: lengths.length,\n        }),\n    );\n\n    // Add DATA stream\n    streams.push(\n        createStream(PhysicalStreamType.DATA, stringBytes, {\n            logical: { dictionaryType: DictionaryType.SINGLE },\n        }),\n    );\n\n    return concatenateBuffers(...streams);\n}\n\nfunction createStream(\n    physicalType: PhysicalStreamType,\n    data: Uint8Array,\n    options: {\n        logical?: LogicalStreamType;\n        technique?: PhysicalLevelTechnique;\n        count?: number;\n    } = {},\n): Uint8Array {\n    const count = options.count ?? 0;\n    return buildEncodedStream(\n        {\n            physicalStreamType: physicalType,\n            logicalStreamType: options.logical ?? {},\n            logicalLevelTechnique1: LogicalLevelTechnique.NONE,\n            logicalLevelTechnique2: LogicalLevelTechnique.NONE,\n            physicalLevelTechnique: options.technique ?? PhysicalLevelTechnique.NONE,\n            numValues: count,\n            byteLength: data.length,\n            decompressedCount: count,\n        },\n        data,\n    );\n}\n\nfunction buildEncodedStream(streamMetadata: StreamMetadata, encodedData: Uint8Array): Uint8Array {\n    const updatedMetadata = {\n        ...streamMetadata,\n        byteLength: encodedData.length,\n    };\n\n    const metadataBuffer = encodeStreamMetadata(updatedMetadata);\n    const result = new Uint8Array(metadataBuffer.length + encodedData.length);\n    result.set(metadataBuffer, 0);\n    result.set(encodedData, metadataBuffer.length);\n\n    return result;\n}\n\nfunction encodeStreamMetadata(metadata: StreamMetadata): Uint8Array {\n    const buffer = new Uint8Array(100);\n    let writeOffset = 0;\n\n    // Byte 1: Stream type\n    const physicalTypeIndex = Object.values(PhysicalStreamType).indexOf(metadata.physicalStreamType);\n    const lowerNibble = getLogicalSubtypeValue(metadata);\n    buffer[writeOffset++] = (physicalTypeIndex << 4) | lowerNibble;\n\n    // Byte 2: Encoding techniques\n    const llt1Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique1);\n    const llt2Index = Object.values(LogicalLevelTechnique).indexOf(metadata.logicalLevelTechnique2);\n    const pltIndex = Object.values(PhysicalLevelTechnique).indexOf(metadata.physicalLevelTechnique);\n    buffer[writeOffset++] = (llt1Index << 5) | (llt2Index << 2) | pltIndex;\n\n    // Variable-length fields\n    const offset = new IntWrapper(writeOffset);\n    encodeVarintInt32Value(metadata.numValues, buffer, offset);\n    encodeVarintInt32Value(metadata.byteLength, buffer, offset);\n\n    return buffer.slice(0, offset.get());\n}\n\nfunction getLogicalSubtypeValue(metadata: StreamMetadata): number {\n    const { physicalStreamType, logicalStreamType } = metadata;\n\n    switch (physicalStreamType) {\n        case PhysicalStreamType.DATA:\n            return logicalStreamType.dictionaryType !== undefined\n                ? Object.values(DictionaryType).indexOf(logicalStreamType.dictionaryType)\n                : 0;\n        case PhysicalStreamType.OFFSET:\n            return logicalStreamType.offsetType !== undefined\n                ? Object.values(OffsetType).indexOf(logicalStreamType.offsetType)\n                : 0;\n        case PhysicalStreamType.LENGTH:\n            return logicalStreamType.lengthType !== undefined\n                ? Object.values(LengthType).indexOf(logicalStreamType.lengthType)\n                : 0;\n        default:\n            return 0;\n    }\n}\n"
  },
  {
    "path": "ts/src/encoding/zOrderCurveEncoder.ts",
    "content": "export function encodeZOrderCurve(x: number, y: number, numBits: number, coordinateShift: number): number {\n    const shiftedX = x + coordinateShift;\n    const shiftedY = y + coordinateShift;\n    let code = 0;\n    for (let i = 0; i < numBits; i++) {\n        code |= ((shiftedX & (1 << i)) << i) | ((shiftedY & (1 << i)) << (i + 1));\n    }\n    return code;\n}\n"
  },
  {
    "path": "ts/src/index.ts",
    "content": "export { default as decodeTile } from \"./mltDecoder\";\nexport { default as FeatureTable } from \"./vector/featureTable\";\nexport { GeometryVector } from \"./vector/geometry/geometryVector\";\nexport { GpuVector } from \"./vector/geometry/gpuVector\";\nexport { default as GeometryScaling } from \"./decoding/geometryScaling\";\nexport { GEOMETRY_TYPE } from \"./vector/geometry/geometryType\";\nexport type { TileSetMetadata } from \"./metadata/tileset/tilesetMetadata\";\nexport type { Geometry } from \"./vector/geometry/geometryVector\";\nexport type { Feature } from \"./vector/featureTable\";\n"
  },
  {
    "path": "ts/src/metadata/tile/dictionaryType.ts",
    "content": "export enum DictionaryType {\n    NONE = \"NONE\",\n    SINGLE = \"SINGLE\",\n    SHARED = \"SHARED\",\n    VERTEX = \"VERTEX\",\n    MORTON = \"MORTON\",\n    FSST = \"FSST\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/lengthType.ts",
    "content": "export enum LengthType {\n    VAR_BINARY = \"VAR_BINARY\",\n    GEOMETRIES = \"GEOMETRIES\",\n    PARTS = \"PARTS\",\n    RINGS = \"RINGS\",\n    TRIANGLES = \"TRIANGLES\",\n    SYMBOL = \"SYMBOL\",\n    DICTIONARY = \"DICTIONARY\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/logicalLevelTechnique.ts",
    "content": "export enum LogicalLevelTechnique {\n    NONE = \"NONE\",\n    DELTA = \"DELTA\",\n    COMPONENTWISE_DELTA = \"COMPONENTWISE_DELTA\",\n    RLE = \"RLE\",\n    MORTON = \"MORTON\",\n    // Pseudodecimal Encoding of floats -> only for the exponent integer part an additional logical level technique is used.\n    // Both exponent and significant parts are encoded with the same physical level technique\n    PDE = \"PDE\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/logicalStreamType.ts",
    "content": "import type { DictionaryType } from \"./dictionaryType\";\nimport type { OffsetType } from \"./offsetType\";\nimport type { LengthType } from \"./lengthType\";\n\nexport type LogicalStreamType = {\n    readonly dictionaryType?: DictionaryType;\n    readonly offsetType?: OffsetType;\n    readonly lengthType?: LengthType;\n};\n"
  },
  {
    "path": "ts/src/metadata/tile/offsetType.ts",
    "content": "export enum OffsetType {\n    VERTEX = \"VERTEX\",\n    INDEX = \"INDEX\",\n    STRING = \"STRING\",\n    KEY = \"KEY\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/physicalLevelTechnique.ts",
    "content": "export enum PhysicalLevelTechnique {\n    NONE = \"NONE\",\n    /**\n     * Preferred option, tends to produce the best compression ratio and decoding performance.\n     * But currently only limited to 32 bit integer.\n     */\n    FAST_PFOR = \"FAST_PFOR\",\n    /**\n     * Can produce better results in combination with a heavyweight compression scheme like Gzip.\n     * Simple compression scheme where the decoder are easier to implement compared to FastPfor.\n     */\n    VARINT = \"VARINT\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/physicalStreamType.ts",
    "content": "export enum PhysicalStreamType {\n    PRESENT = \"PRESENT\",\n    DATA = \"DATA\",\n    OFFSET = \"OFFSET\",\n    LENGTH = \"LENGTH\",\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/scalarType.ts",
    "content": "export enum ScalarType {\n    BOOLEAN = 0,\n    INT_8 = 1,\n    UINT_8 = 2,\n    INT_32 = 3,\n    UINT_32 = 4,\n    INT_64 = 5,\n    UINT_64 = 6,\n    FLOAT = 7,\n    DOUBLE = 8,\n    STRING = 9,\n}\n"
  },
  {
    "path": "ts/src/metadata/tile/streamMetadataDecoder.ts",
    "content": "import { LogicalLevelTechnique } from \"./logicalLevelTechnique\";\nimport { PhysicalLevelTechnique } from \"./physicalLevelTechnique\";\nimport { decodeVarintInt32 } from \"../../decoding/integerDecodingUtils\";\nimport { PhysicalStreamType } from \"./physicalStreamType\";\nimport { DictionaryType } from \"./dictionaryType\";\nimport { OffsetType } from \"./offsetType\";\nimport { LengthType } from \"./lengthType\";\nimport type { LogicalStreamType } from \"./logicalStreamType\";\nimport type IntWrapper from \"../../decoding/intWrapper\";\n\nexport type StreamMetadata = {\n    readonly physicalStreamType: PhysicalStreamType;\n    readonly logicalStreamType: LogicalStreamType;\n    readonly logicalLevelTechnique1: LogicalLevelTechnique;\n    readonly logicalLevelTechnique2: LogicalLevelTechnique;\n    readonly physicalLevelTechnique: PhysicalLevelTechnique;\n    readonly numValues: number;\n    readonly byteLength: number;\n    /**\n     * Returns the number of decompressed values.\n     * For non-RLE streams, this is the same as numValues.\n     * For RLE streams, this is overridden to return numRleValues.\n     */\n    readonly decompressedCount: number;\n};\n\nexport type MortonEncodedStreamMetadata = StreamMetadata & {\n    readonly numBits: number;\n    readonly coordinateShift: number;\n};\n\nexport type RleEncodedStreamMetadata = StreamMetadata & {\n    readonly runs: number;\n    readonly numRleValues: number;\n};\n\nexport function decodeStreamMetadata(tile: Uint8Array, offset: IntWrapper): StreamMetadata {\n    const streamMetadata = decodeStreamMetadataInternal(tile, offset);\n    if (streamMetadata.logicalLevelTechnique1 === LogicalLevelTechnique.MORTON) {\n        return decodePartialMortonEncodedStreamMetadata(streamMetadata, tile, offset);\n    }\n\n    if (\n        (LogicalLevelTechnique.RLE === streamMetadata.logicalLevelTechnique1 ||\n            LogicalLevelTechnique.RLE === streamMetadata.logicalLevelTechnique2) &&\n        PhysicalLevelTechnique.NONE !== streamMetadata.physicalLevelTechnique\n    ) {\n        return decodePartialRleEncodedStreamMetadata(streamMetadata, tile, offset);\n    }\n\n    return streamMetadata;\n}\n\nfunction decodePartialMortonEncodedStreamMetadata(\n    streamMetadata: StreamMetadata,\n    tile: Uint8Array,\n    offset: IntWrapper,\n): MortonEncodedStreamMetadata {\n    const mortonInfo = decodeVarintInt32(tile, offset, 2);\n    return {\n        physicalStreamType: streamMetadata.physicalStreamType,\n        logicalStreamType: streamMetadata.logicalStreamType,\n        logicalLevelTechnique1: streamMetadata.logicalLevelTechnique1,\n        logicalLevelTechnique2: streamMetadata.logicalLevelTechnique2,\n        physicalLevelTechnique: streamMetadata.physicalLevelTechnique,\n        numValues: streamMetadata.numValues,\n        byteLength: streamMetadata.byteLength,\n        decompressedCount: streamMetadata.decompressedCount,\n        numBits: mortonInfo[0],\n        coordinateShift: mortonInfo[1],\n    };\n}\n\nfunction decodePartialRleEncodedStreamMetadata(\n    streamMetadata: StreamMetadata,\n    tile: Uint8Array,\n    offset: IntWrapper,\n): RleEncodedStreamMetadata {\n    const rleInfo = decodeVarintInt32(tile, offset, 2);\n    return {\n        physicalStreamType: streamMetadata.physicalStreamType,\n        logicalStreamType: streamMetadata.logicalStreamType,\n        logicalLevelTechnique1: streamMetadata.logicalLevelTechnique1,\n        logicalLevelTechnique2: streamMetadata.logicalLevelTechnique2,\n        physicalLevelTechnique: streamMetadata.physicalLevelTechnique,\n        numValues: streamMetadata.numValues,\n        byteLength: streamMetadata.byteLength,\n        decompressedCount: rleInfo[1],\n        runs: rleInfo[0],\n        numRleValues: rleInfo[1],\n    };\n}\n\nfunction decodeStreamMetadataInternal(tile: Uint8Array, offset: IntWrapper): StreamMetadata {\n    const stream_type = tile[offset.get()];\n    const physicalStreamType = Object.values(PhysicalStreamType)[stream_type >> 4] as PhysicalStreamType;\n    let logicalStreamType: LogicalStreamType | null = null;\n\n    switch (physicalStreamType) {\n        case PhysicalStreamType.DATA:\n            logicalStreamType = {\n                dictionaryType: Object.values(DictionaryType)[stream_type & 0xf],\n            };\n            break;\n        case PhysicalStreamType.OFFSET:\n            logicalStreamType = {\n                offsetType: Object.values(OffsetType)[stream_type & 0xf],\n            };\n            break;\n        case PhysicalStreamType.LENGTH:\n            logicalStreamType = {\n                lengthType: Object.values(LengthType)[stream_type & 0xf],\n            };\n            break;\n    }\n    offset.increment();\n\n    const encodings_header = tile[offset.get()];\n    const llt1 = Object.values(LogicalLevelTechnique)[encodings_header >> 5] as LogicalLevelTechnique;\n    const llt2 = Object.values(LogicalLevelTechnique)[(encodings_header >> 2) & 0x7] as LogicalLevelTechnique;\n    const plt = Object.values(PhysicalLevelTechnique)[encodings_header & 0x3] as PhysicalLevelTechnique;\n    offset.increment();\n\n    const sizeInfo = decodeVarintInt32(tile, offset, 2);\n    const numValues = sizeInfo[0];\n    const byteLength = sizeInfo[1];\n\n    return {\n        physicalStreamType,\n        logicalStreamType,\n        logicalLevelTechnique1: llt1,\n        logicalLevelTechnique2: llt2,\n        physicalLevelTechnique: plt,\n        numValues,\n        byteLength,\n        decompressedCount: numValues,\n    };\n}\n"
  },
  {
    "path": "ts/src/metadata/tileset/embeddedTilesetMetadataDecoder.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { decodeField, decodeEmbeddedTileSetMetadata } from \"./embeddedTilesetMetadataDecoder\";\nimport IntWrapper from \"../../decoding/intWrapper\";\nimport { concatenateBuffers } from \"../../decoding/decodingTestUtils\";\nimport { ComplexType, LogicalScalarType, ScalarType } from \"./tilesetMetadata\";\nimport {\n    encodeChildCount,\n    encodeFieldName,\n    encodeTypeCode,\n    scalarTypeCode,\n} from \"../../encoding/embeddedTilesetMetadataEncoder\";\n\nconst STRUCT_TYPE_CODE = 30;\n\ndescribe(\"embeddedTilesetMetadataDecoder\", () => {\n    describe(\"decodeField\", () => {\n        describe(\"scalar fields\", () => {\n            it(\"should decode non-nullable STRING field\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.STRING, false)),\n                    encodeFieldName(\"street\"),\n                );\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"street\");\n                expect(field.nullable).toBe(false);\n                expect(field.type).toBe(\"scalarField\");\n                expect(field.scalarField?.physicalType).toBe(ScalarType.STRING);\n            });\n\n            it(\"should decode nullable UINT_64 field\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.UINT_64, true)),\n                    encodeFieldName(\"population\"),\n                );\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"population\");\n                expect(field.nullable).toBe(true);\n                expect(field.type).toBe(\"scalarField\");\n                expect(field.scalarField?.physicalType).toBe(ScalarType.UINT_64);\n            });\n\n            it(\"should decode BOOLEAN field\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.BOOLEAN, false)),\n                    encodeFieldName(\"isActive\"),\n                );\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"isActive\");\n                expect(field.nullable).toBe(false);\n                expect(field.type).toBe(\"scalarField\");\n                expect(field.scalarField?.physicalType).toBe(ScalarType.BOOLEAN);\n            });\n\n            it(\"should decode non-nullable UINT_32 field\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.UINT_32, false)),\n                    encodeFieldName(\"count\"),\n                );\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"count\");\n                expect(field.nullable).toBe(false);\n                expect(field.type).toBe(\"scalarField\");\n                expect(field.scalarField?.physicalType).toBe(ScalarType.UINT_32);\n            });\n\n            it(\"should decode nullable FLOAT field\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.FLOAT, true)),\n                    encodeFieldName(\"temperature\"),\n                );\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"temperature\");\n                expect(field.nullable).toBe(true);\n                expect(field.type).toBe(\"scalarField\");\n                expect(field.scalarField?.physicalType).toBe(ScalarType.FLOAT);\n            });\n        });\n\n        describe(\"complex fields\", () => {\n            it(\"should decode STRUCT field with nested children\", () => {\n                const children = [\n                    {\n                        typeCode: scalarTypeCode(ScalarType.STRING, false),\n                        name: \"street\",\n                        nullable: false,\n                        physicalType: ScalarType.STRING,\n                    },\n                    {\n                        typeCode: scalarTypeCode(ScalarType.UINT_32, true),\n                        name: \"zipcode\",\n                        nullable: true,\n                        physicalType: ScalarType.UINT_32,\n                    },\n                ];\n\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(STRUCT_TYPE_CODE),\n                    encodeFieldName(\"address\"),\n                    encodeChildCount(children.length),\n                    ...children.flatMap((c) => [encodeTypeCode(c.typeCode), encodeFieldName(c.name)]),\n                );\n\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"address\");\n                expect(field.nullable).toBe(false);\n                expect(field.type).toBe(\"complexField\");\n                expect(field.complexField?.physicalType).toBe(ComplexType.STRUCT);\n                expect(field.complexField?.children).toHaveLength(children.length);\n\n                for (let i = 0; i < children.length; i++) {\n                    const child = children[i];\n\n                    expect(field.complexField?.children[i].name).toBe(child.name);\n                    expect(field.complexField?.children[i].nullable).toBe(child.nullable);\n                    expect(field.complexField?.children[i].scalarField?.physicalType).toBe(child.physicalType);\n                }\n            });\n        });\n\n        describe(\"deeply nested structures\", () => {\n            it(\"should decode 3-level nested STRUCT\", () => {\n                const leafChildren = [\n                    { typeCode: scalarTypeCode(ScalarType.FLOAT, false), name: \"lat\" },\n                    { typeCode: scalarTypeCode(ScalarType.FLOAT, false), name: \"lon\" },\n                ];\n\n                const buffer = concatenateBuffers(\n                    // Parent STRUCT \"location\"\n                    encodeTypeCode(STRUCT_TYPE_CODE),\n                    encodeFieldName(\"location\"),\n                    encodeChildCount(1),\n                    // Child STRUCT \"address\"\n                    encodeTypeCode(STRUCT_TYPE_CODE),\n                    encodeFieldName(\"address\"),\n                    encodeChildCount(1),\n                    // Grandchild STRUCT \"coordinates\"\n                    encodeTypeCode(STRUCT_TYPE_CODE),\n                    encodeFieldName(\"coordinates\"),\n                    encodeChildCount(leafChildren.length),\n                    // Great-grandchildren\n                    ...leafChildren.flatMap((c) => [encodeTypeCode(c.typeCode), encodeFieldName(c.name)]),\n                );\n\n                const field = decodeField(buffer, new IntWrapper(0));\n\n                expect(field.name).toBe(\"location\");\n                expect(field.type).toBe(\"complexField\");\n                expect(field.complexField?.physicalType).toBe(ComplexType.STRUCT);\n\n                const address = field.complexField?.children[0];\n                expect(address?.name).toBe(\"address\");\n                expect(address?.type).toBe(\"complexField\");\n\n                const coordinates = address?.complexField?.children[0];\n                expect(coordinates?.name).toBe(\"coordinates\");\n                expect(coordinates?.complexField?.children).toHaveLength(leafChildren.length);\n\n                for (let i = 0; i < leafChildren.length; i++) {\n                    const child = leafChildren[i];\n\n                    expect(coordinates?.complexField?.children[i].name).toBe(child.name);\n                    expect(coordinates?.complexField?.children[i].scalarField?.physicalType).toBe(ScalarType.FLOAT);\n                }\n            });\n        });\n\n        describe(\"offset tracking\", () => {\n            it(\"should correctly advance offset\", () => {\n                const buffer = concatenateBuffers(\n                    encodeTypeCode(scalarTypeCode(ScalarType.STRING, false)),\n                    encodeFieldName(\"test\"),\n                );\n                const offset = new IntWrapper(0);\n\n                decodeField(buffer, offset);\n\n                expect(offset.get()).toBe(buffer.length);\n            });\n        });\n\n        describe(\"error handling\", () => {\n            it(\"should throw error for unsupported typeCode\", () => {\n                const buffer = encodeTypeCode(999);\n\n                expect(() => {\n                    decodeField(buffer, new IntWrapper(0));\n                }).toThrow(\"Unsupported field type code 999. Supported: 10-29(scalars), 30(STRUCT)\");\n            });\n        });\n    });\n\n    describe(\"decodeEmbeddedTileSetMetadata\", () => {\n        it(\"should decode tileset with STRUCT column\", () => {\n            const buffer = concatenateBuffers(\n                encodeFieldName(\"\"),\n                encodeTypeCode(4096),\n                encodeChildCount(1),\n                encodeTypeCode(STRUCT_TYPE_CODE),\n                encodeFieldName(\"props\"),\n                encodeChildCount(1),\n                encodeTypeCode(scalarTypeCode(ScalarType.STRING, false)),\n                encodeFieldName(\"name\"),\n            );\n\n            const [metadata, extent] = decodeEmbeddedTileSetMetadata(buffer, new IntWrapper(0));\n\n            expect(extent).toBe(4096);\n            expect(metadata.featureTables[0].name).toBe(\"\");\n            expect(metadata.featureTables[0].columns[0].complexType.children).toHaveLength(1);\n        });\n\n        it(\"should decode logical ID metadata with implicit id column name\", () => {\n            const typeCode = 3;\n            const buffer = concatenateBuffers(\n                encodeFieldName(\"layer\"),\n                encodeTypeCode(4096),\n                encodeChildCount(1),\n                encodeTypeCode(typeCode),\n            );\n\n            const [metadata] = decodeEmbeddedTileSetMetadata(buffer, new IntWrapper(0));\n            const idColumn = metadata.featureTables[0].columns[0];\n\n            expect(idColumn.name).toBe(\"id\");\n            expect(idColumn.nullable).toBe(true);\n            expect(idColumn.type).toBe(\"scalarType\");\n            expect(idColumn.scalarType?.type).toBe(\"logicalType\");\n            expect(idColumn.scalarType?.logicalType).toBe(LogicalScalarType.ID);\n            expect(idColumn.scalarType?.longID).toBe(true);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/metadata/tileset/embeddedTilesetMetadataDecoder.ts",
    "content": "import type IntWrapper from \"../../decoding/intWrapper\";\nimport { decodeVarintInt32 } from \"../../decoding/integerDecodingUtils\";\nimport type { Column, FeatureTableSchema, Field, TileSetMetadata } from \"./tilesetMetadata\";\nimport { columnTypeHasChildren, columnTypeHasName, decodeColumnType } from \"./typeMap\";\n\nconst textDecoder = new TextDecoder();\n\nconst SUPPORTED_COLUMN_TYPES = \"0-3(ID), 4(GEOMETRY), 10-29(scalars), 30(STRUCT)\";\nconst SUPPORTED_FIELD_TYPES = \"10-29(scalars), 30(STRUCT)\";\n\n/**\n * Decodes a length-prefixed UTF-8 string.\n * Layout: [len: varint32][bytes: len]\n */\nfunction decodeString(src: Uint8Array, offset: IntWrapper): string {\n    const length = decodeVarintInt32(src, offset, 1)[0];\n    if (length === 0) {\n        return \"\";\n    }\n    const start = offset.get();\n    const end = start + length;\n    const view = src.subarray(start, end);\n    offset.add(length);\n    return textDecoder.decode(view);\n}\n\n/**\n * Converts a Column to a Field.\n * Used when decoding Field metadata which has the same format as Column.\n */\nfunction columnToField(column: Column): Field {\n    return {\n        name: column.name,\n        nullable: column.nullable,\n        scalarField: column.scalarType,\n        complexField: column.complexType,\n        type: column.type === \"scalarType\" ? \"scalarField\" : \"complexField\",\n    };\n}\n\n/**\n * Decodes a Field used as part of complex types (STRUCT children).\n */\nexport function decodeField(src: Uint8Array, offset: IntWrapper): Field {\n    const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;\n\n    if (typeCode < 10 || typeCode > 30) {\n        throw new Error(`Unsupported field type code ${typeCode}. Supported: ${SUPPORTED_FIELD_TYPES}`);\n    }\n\n    const column = decodeColumnType(typeCode);\n\n    if (columnTypeHasName(typeCode)) {\n        column.name = decodeString(src, offset);\n    }\n\n    if (columnTypeHasChildren(typeCode)) {\n        const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;\n        column.complexType.children = new Array(childCount);\n        for (let i = 0; i < childCount; i++) {\n            column.complexType.children[i] = decodeField(src, offset);\n        }\n    }\n\n    return columnToField(column);\n}\n\n/**\n * The typeCode encodes the column type, nullable flag, and whether it has name/children.\n */\nfunction decodeColumn(src: Uint8Array, offset: IntWrapper): Column {\n    const typeCode = decodeVarintInt32(src, offset, 1)[0] >>> 0;\n    const column = decodeColumnType(typeCode);\n\n    if (!column) {\n        throw new Error(`Unsupported column type code ${typeCode}. Supported: ${SUPPORTED_COLUMN_TYPES}`);\n    }\n\n    if (columnTypeHasName(typeCode)) {\n        column.name = decodeString(src, offset);\n    } else {\n        // ID and GEOMETRY columns have implicit names\n        if (typeCode >= 0 && typeCode <= 3) {\n            column.name = \"id\";\n        } else if (typeCode === 4) {\n            column.name = \"geometry\";\n        }\n    }\n\n    if (columnTypeHasChildren(typeCode)) {\n        // Only STRUCT (typeCode 30) has children\n        const childCount = decodeVarintInt32(src, offset, 1)[0] >>> 0;\n        const complexCol = column.complexType;\n        complexCol.children = new Array(childCount);\n        for (let i = 0; i < childCount; i++) {\n            complexCol.children[i] = decodeField(src, offset);\n        }\n    }\n\n    return column;\n}\n\n/**\n * Top-level decoder for embedded tileset metadata.\n * Reads exactly ONE FeatureTableSchema from the stream.\n *\n * @param bytes The byte array containing the metadata\n * @param offset The current offset in the byte array (will be advanced)\n */\nexport function decodeEmbeddedTileSetMetadata(bytes: Uint8Array, offset: IntWrapper): [TileSetMetadata, number] {\n    const meta = {} as TileSetMetadata;\n    meta.featureTables = [];\n\n    const table = {} as FeatureTableSchema;\n    table.name = decodeString(bytes, offset);\n    const extent = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;\n\n    const columnCount = decodeVarintInt32(bytes, offset, 1)[0] >>> 0;\n    table.columns = new Array(columnCount);\n    for (let j = 0; j < columnCount; j++) {\n        table.columns[j] = decodeColumn(bytes, offset);\n    }\n\n    meta.featureTables.push(table);\n\n    return [meta, extent];\n}\n"
  },
  {
    "path": "ts/src/metadata/tileset/tilesetMetadata.ts",
    "content": "// based on ../spec/schema/mlt_tileset_metadata.proto\nexport const ColumnScope = {\n    FEATURE: 0,\n    VERTEX: 1,\n} as const;\n\nexport const ScalarType = {\n    BOOLEAN: 0,\n    INT_8: 1,\n    UINT_8: 2,\n    INT_32: 3,\n    UINT_32: 4,\n    INT_64: 5,\n    UINT_64: 6,\n    FLOAT: 7,\n    DOUBLE: 8,\n    STRING: 9,\n} as const;\n\nexport const ComplexType = {\n    GEOMETRY: 0,\n    STRUCT: 1,\n} as const;\n\nexport const LogicalScalarType = {\n    ID: 0,\n} as const;\n\nexport const LogicalComplexType = {\n    BINARY: 0,\n    RANGE_MAP: 1,\n} as const;\n\nexport interface TileSetMetadata {\n    version?: number | null;\n    featureTables: FeatureTableSchema[];\n    name?: string | null;\n    description?: string | null;\n    attribution?: string | null;\n    minZoom?: number | null;\n    maxZoom?: number | null;\n    bounds: number[];\n    center: number[];\n}\n\nexport interface FeatureTableSchema {\n    name?: string | null;\n    columns: Column[];\n}\n\nexport interface Column {\n    name?: string | null;\n    nullable?: boolean | null;\n    columnScope?: number | null;\n    scalarType?: ScalarColumn | null;\n    complexType?: ComplexColumn | null;\n    type?: \"scalarType\" | \"complexType\";\n}\n\nexport interface ScalarColumn {\n    longID?: boolean | null;\n    physicalType?: number | null;\n    logicalType?: number | null;\n    type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface ComplexColumn {\n    physicalType?: number | null;\n    logicalType?: number | null;\n    children: Field[];\n    type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface Field {\n    name?: string | null;\n    nullable?: boolean | null;\n    scalarField?: ScalarField | null;\n    complexField?: ComplexField | null;\n    type?: \"scalarField\" | \"complexField\";\n}\n\nexport interface ScalarField {\n    physicalType?: number | null;\n    logicalType?: number | null;\n    type?: \"physicalType\" | \"logicalType\";\n}\n\nexport interface ComplexField {\n    physicalType?: number | null;\n    logicalType?: number | null;\n    children: Field[];\n    type?: \"physicalType\" | \"logicalType\";\n}\n"
  },
  {
    "path": "ts/src/metadata/tileset/typeMap.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { type Column, ComplexType, LogicalScalarType, ScalarType } from \"./tilesetMetadata\";\nimport { decodeColumnType, isGeometryColumn, isLogicalIdColumn } from \"./typeMap\";\n\ndescribe(\"typeMap helpers\", () => {\n    it(\"should decode ID type codes with logical metadata and bit flags\", () => {\n        const cases = [\n            { typeCode: 0, nullable: false, longID: false },\n            { typeCode: 1, nullable: true, longID: false },\n            { typeCode: 2, nullable: false, longID: true },\n            { typeCode: 3, nullable: true, longID: true },\n        ];\n\n        for (const { typeCode, nullable, longID } of cases) {\n            const column = decodeColumnType(typeCode);\n\n            expect(column).toMatchObject({\n                nullable,\n                type: \"scalarType\",\n                scalarType: {\n                    type: \"logicalType\",\n                    logicalType: LogicalScalarType.ID,\n                    longID,\n                },\n            });\n            expect(isLogicalIdColumn(column)).toBe(true);\n        }\n    });\n\n    it(\"should return false for physical scalar columns even when column is named id\", () => {\n        const physicalIdNamedColumn = {\n            name: \"id\",\n            type: \"scalarType\",\n            scalarType: {\n                type: \"physicalType\",\n                physicalType: ScalarType.UINT_32,\n            },\n        } as Column;\n\n        expect(isLogicalIdColumn(physicalIdNamedColumn)).toBe(false);\n    });\n\n    it(\"should return true for logical ID columns regardless of column name\", () => {\n        const logicalIdAnyNameColumn = {\n            name: \"my_custom_id\",\n            type: \"scalarType\",\n            scalarType: {\n                type: \"logicalType\",\n                logicalType: LogicalScalarType.ID,\n                longID: true,\n            },\n        } as Column;\n\n        expect(isLogicalIdColumn(logicalIdAnyNameColumn)).toBe(true);\n    });\n\n    it(\"should return false for STRUCT columns even when column is named geometry\", () => {\n        const structNamedGeometryColumn = {\n            name: \"geometry\",\n            type: \"complexType\",\n            complexType: {\n                type: \"physicalType\",\n                physicalType: ComplexType.STRUCT,\n                children: [],\n            },\n        } as Column;\n\n        expect(isGeometryColumn(structNamedGeometryColumn)).toBe(false);\n    });\n\n    it(\"should return true for GEOMETRY columns regardless of column name\", () => {\n        const geometryAnyNameColumn = {\n            name: \"geom\",\n            type: \"complexType\",\n            complexType: {\n                type: \"physicalType\",\n                physicalType: ComplexType.GEOMETRY,\n                children: [],\n            },\n        } as Column;\n\n        expect(isGeometryColumn(geometryAnyNameColumn)).toBe(true);\n    });\n});\n"
  },
  {
    "path": "ts/src/metadata/tileset/typeMap.ts",
    "content": "import {\n    type Column,\n    ColumnScope,\n    type ComplexColumn,\n    ComplexType,\n    LogicalScalarType,\n    type ScalarColumn,\n    ScalarType,\n} from \"./tilesetMetadata\";\n\n/**\n * The type code is a single varint32 that encodes:\n * - Physical or logical type\n * - Nullable flag\n * - Whether the column has a name (typeCode >= 10)\n * - Whether the column has children (typeCode == 30 for STRUCT)\n * - For ID types: whether it uses long (64-bit) IDs\n */\n\n/**\n * Decodes a type code into a Column structure.\n *\n * ID type codes (0..3):\n * - Bit 0: nullable\n * - Bit 1: longID (0/1 -> uint32 IDs, 2/3 -> uint64 IDs)\n *\n * ID columns are kept as logical types so they remain distinguishable\n * from feature properties that may also be named \"id\".\n */\nexport function decodeColumnType(typeCode: number): Column | null {\n    switch (typeCode) {\n        case 0:\n        case 1:\n        case 2:\n        case 3: {\n            const column = {} as Column;\n            column.nullable = (typeCode & 1) !== 0;\n            column.columnScope = ColumnScope.FEATURE;\n            const scalarCol = {} as ScalarColumn;\n            scalarCol.type = \"logicalType\";\n            scalarCol.logicalType = LogicalScalarType.ID;\n            scalarCol.longID = (typeCode & 2) !== 0;\n            column.scalarType = scalarCol;\n            column.type = \"scalarType\";\n            return column;\n        }\n        case 4: {\n            // GEOMETRY (non-nullable, no children)\n            const column = {} as Column;\n            column.nullable = false;\n            column.columnScope = ColumnScope.FEATURE;\n            const complexCol = {} as ComplexColumn;\n            complexCol.type = \"physicalType\";\n            complexCol.physicalType = ComplexType.GEOMETRY;\n            column.type = \"complexType\";\n            column.complexType = complexCol;\n            return column;\n        }\n        case 30: {\n            // STRUCT (non-nullable with children)\n            const column = {} as Column;\n            column.nullable = false;\n            column.columnScope = ColumnScope.FEATURE;\n            const complexCol = {} as ComplexColumn;\n            complexCol.type = \"physicalType\";\n            complexCol.physicalType = ComplexType.STRUCT;\n            column.type = \"complexType\";\n            column.complexType = complexCol;\n            return column;\n        }\n        default:\n            return mapScalarType(typeCode);\n    }\n}\n\n/**\n * Returns true if this type code requires a name to be stored.\n * ID (0-3) and GEOMETRY (4) columns have implicit names.\n * All other types (>= 10) require explicit names.\n */\nexport function columnTypeHasName(typeCode: number): boolean {\n    return typeCode >= 10;\n}\n\n/**\n * Returns true if this type code has child fields.\n * Only STRUCT (typeCode 30) has children.\n */\nexport function columnTypeHasChildren(typeCode: number): boolean {\n    return typeCode === 30;\n}\n\n/**\n * Determines if a stream count needs to be read for this column.\n * Mirrors the logic in cpp/include/mlt/metadata/type_map.hpp lines 85-122\n */\nexport function hasStreamCount(column: Column): boolean {\n    if (column.type === \"scalarType\") {\n        const scalarCol = column.scalarType;\n\n        if (scalarCol.type === \"physicalType\") {\n            const physicalType = scalarCol.physicalType;\n            switch (physicalType) {\n                case ScalarType.BOOLEAN:\n                case ScalarType.INT_8:\n                case ScalarType.UINT_8:\n                case ScalarType.INT_32:\n                case ScalarType.UINT_32:\n                case ScalarType.INT_64:\n                case ScalarType.UINT_64:\n                case ScalarType.FLOAT:\n                case ScalarType.DOUBLE:\n                    return false;\n                case ScalarType.STRING:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n        if (scalarCol.type === \"logicalType\") {\n            return false;\n        }\n    } else if (column.type === \"complexType\") {\n        const complexCol = column.complexType;\n\n        if (complexCol.type === \"physicalType\") {\n            const physicalType = complexCol.physicalType;\n            switch (physicalType) {\n                case ComplexType.GEOMETRY:\n                case ComplexType.STRUCT:\n                    return true;\n                default:\n                    return false;\n            }\n        }\n    }\n\n    console.warn(\"Unexpected column type in hasStreamCount\", column);\n    return false;\n}\n\nexport function isLogicalIdColumn(column: Column): boolean {\n    return (\n        column.type === \"scalarType\" &&\n        column.scalarType?.type === \"logicalType\" &&\n        column.scalarType.logicalType === LogicalScalarType.ID\n    );\n}\n\nexport function isGeometryColumn(column: Column): boolean {\n    return (\n        column.type === \"complexType\" &&\n        column.complexType?.type === \"physicalType\" &&\n        column.complexType.physicalType === ComplexType.GEOMETRY\n    );\n}\n\n/**\n * Maps a scalar type code to a Column with ScalarType.\n * Type codes 10-29 encode scalar types with nullable flag.\n * Even codes are non-nullable, odd codes are nullable.\n */\nfunction mapScalarType(typeCode: number): Column | null {\n    let scalarType: number | null;\n\n    switch (typeCode) {\n        case 10:\n        case 11:\n            scalarType = ScalarType.BOOLEAN;\n            break;\n        case 12:\n        case 13:\n            scalarType = ScalarType.INT_8;\n            break;\n        case 14:\n        case 15:\n            scalarType = ScalarType.UINT_8;\n            break;\n        case 16:\n        case 17:\n            scalarType = ScalarType.INT_32;\n            break;\n        case 18:\n        case 19:\n            scalarType = ScalarType.UINT_32;\n            break;\n        case 20:\n        case 21:\n            scalarType = ScalarType.INT_64;\n            break;\n        case 22:\n        case 23:\n            scalarType = ScalarType.UINT_64;\n            break;\n        case 24:\n        case 25:\n            scalarType = ScalarType.FLOAT;\n            break;\n        case 26:\n        case 27:\n            scalarType = ScalarType.DOUBLE;\n            break;\n        case 28:\n        case 29:\n            scalarType = ScalarType.STRING;\n            break;\n        default:\n            return null;\n    }\n\n    const column = {} as Column;\n    column.nullable = (typeCode & 1) !== 0;\n    column.columnScope = ColumnScope.FEATURE;\n    const scalarCol = {} as ScalarColumn;\n    scalarCol.type = \"physicalType\";\n    scalarCol.physicalType = scalarType;\n    column.type = \"scalarType\";\n    column.scalarType = scalarCol;\n    return column;\n}\n"
  },
  {
    "path": "ts/src/mltDecoder.spec.ts",
    "content": "import assert from \"node:assert/strict\";\nimport { describe, it } from \"vitest\";\nimport { readdirSync, readFileSync } from \"node:fs\";\nimport { parse, join } from \"node:path\";\nimport { VectorTile, type VectorTileFeature } from \"@mapbox/vector-tile\";\nimport Pbf from \"pbf\";\n\nimport { type FeatureTable, type Feature, decodeTile } from \".\";\nimport path from \"node:path\";\nimport fs from \"node:fs\";\n\nconst ITERATOR_TILE = path.resolve(__dirname, \"../../test/expected/tag0x01/simple/multiline-boolean.mlt\");\n\ndescribe(\"MLT Decoder - MVT comparison for SIMPLE tiles\", () => {\n    const simpleMltTileDir = \"../test/expected/tag0x01/simple\";\n    const simpleMvtTileDir = \"../test/fixtures/simple\";\n    testTiles(simpleMltTileDir, simpleMvtTileDir);\n});\n\ndescribe(\"MLT Decoder - MVT comparison for Amazon tiles\", () => {\n    const amazonMltTileDir = \"../test/expected/tag0x01/amazon\";\n    const amazonMvtTileDir = \"../test/fixtures/amazon\";\n    testTiles(amazonMltTileDir, amazonMvtTileDir);\n});\n\ndescribe(\"MLT Decoder - MVT comparison for OMT tiles\", () => {\n    const omtMltTileDir = \"../test/expected/tag0x01/omt\";\n    const omtMvtTileDir = \"../test/fixtures/omt\";\n    testTiles(omtMltTileDir, omtMvtTileDir);\n}, 150000);\n\ndescribe(\"MLT Decoder - MVT comparison for Bing tiles\", () => {\n    const bingMltTileDir = \"../test/expected/tag0x01/bing\";\n    const bingMvtTileDir = \"../test/fixtures/bing\";\n    testTiles(bingMltTileDir, bingMvtTileDir);\n}, 150000);\n\ndescribe(\"FeatureTable\", () => {\n    it(\"should iterate through features correctly\", () => {\n        const bytes = new Uint8Array(fs.readFileSync(ITERATOR_TILE));\n        const featureTables = decodeTile(bytes);\n\n        const table = featureTables[0];\n\n        assert.equal(table.name, \"layer\");\n        assert.equal(table.extent, 4096);\n\n        let featureCount = 0;\n        for (const feature of table.getFeatures()) {\n            assert.ok(feature.geometry);\n            assert.ok(Array.isArray(feature.geometry.coordinates));\n            assert.ok(feature.geometry.coordinates.length > 0);\n            assert.equal(typeof feature.geometry.type, \"number\");\n\n            featureCount++;\n        }\n        assert.equal(featureCount, table.numFeatures);\n    });\n});\n\nfunction testTiles(mltSearchDir: string, mvtSearchDir: string) {\n    const mltFileNames = readdirSync(mltSearchDir)\n        .filter((file) => parse(file).ext === \".mlt\")\n        .map((file) => parse(file).name);\n    for (const fileName of mltFileNames) {\n        it(`should compare ${fileName} tile`, () => {\n            const mltFileName = `${fileName}.mlt`;\n            const mltPath = join(mltSearchDir, mltFileName);\n            const mvtPath = join(mvtSearchDir, `${fileName}.mvt`);\n\n            const encodedMvt = readFileSync(mvtPath);\n            const encodedMlt = readFileSync(mltPath);\n            const buf = new Pbf(encodedMvt);\n            const decodedMvt = new VectorTile(buf);\n\n            const decodedMlt = decodeTile(encodedMlt, undefined, true);\n            comparePlainGeometryEncodedTile(decodedMlt, decodedMvt);\n        });\n    }\n}\n\nfunction removeEmptyStrings(mvtProperties: Record<string, any>) {\n    for (const key of Object.keys(mvtProperties)) {\n        const value = mvtProperties[key];\n        if (typeof value === \"string\" && !value.length) {\n            delete mvtProperties[key];\n        }\n    }\n}\n\nfunction comparePlainGeometryEncodedTile(mlt: FeatureTable[], mvt: VectorTile) {\n    for (const featureTable of mlt) {\n        const layer = mvt.layers[featureTable.name];\n\n        // Use getFeatures() instead of iterator (like C++ and Java implementations)\n        const mltFeatures = featureTable.getFeatures();\n\n        assert.equal(mltFeatures.length, layer.length);\n\n        for (let j = 0; j < layer.length; j++) {\n            const mvtFeature = layer.feature(j);\n            const mltFeature = mltFeatures[j];\n\n            compareId(mltFeature, mvtFeature, true);\n\n            const mltGeometry = mltFeature.geometry?.coordinates;\n            const mvtGeometry = mvtFeature.loadGeometry();\n            assert.deepEqual(mltGeometry, mvtGeometry);\n\n            const mltProperties = mltFeature.properties;\n            const mvtProperties = mvtFeature.properties;\n            transformPropertyNames(mltProperties);\n            transformPropertyNames(mvtProperties);\n            convertBigIntPropertyValues(mltProperties);\n            //TODO: fix -> since a change in the java converter shared dictionary encoding empty strings are not\n            //encoded anymore\n            removeEmptyStrings(mvtProperties);\n            removeEmptyStrings(mltProperties);\n            assert.deepEqual(mltProperties, mvtProperties);\n        }\n    }\n}\n\nfunction compareId(mltFeature: Feature, mvtFeature: VectorTileFeature, idWithinMaxSafeInteger: boolean) {\n    if (!mvtFeature.id) {\n        /* Java MVT library in the MVT converter decodes zero for undefined ids */\n        assert.ok(mltFeature.id === 0 || mltFeature.id === null || mltFeature.id === 0n);\n    } else {\n        const mltFeatureId = mltFeature.id;\n        /* For const and sequence vectors the decoder can return bigint compared to the vector-tile-js library */\n        const actualId =\n            idWithinMaxSafeInteger && typeof mltFeatureId !== \"bigint\" ? mltFeatureId : Number(mltFeatureId);\n        /*\n         * The id check can fail for two known reasons:\n         * - The java-vector-tile library used in the Java converter returns negative integers for the\n         *   unoptimized tileset in some tiles\n         * - The vector-tile-js library is using number types for the id so there can only be stored\n         *   values up to 53 bits without loss of precision\n         **/\n        if (mltFeatureId < 0 || mltFeatureId > Number.MAX_SAFE_INTEGER) {\n            /* Expected to fail in some/most cases */\n            try {\n                assert.equal(actualId, mvtFeature.id);\n            } catch (_e) {\n                //console.info(\"id mismatch\", featureTableName, mltFeatureId, mvtFeature.id);\n            }\n            return;\n        }\n\n        if (!Number.isSafeInteger(mvtFeature.id)) {\n            return;\n        }\n\n        assert.equal(actualId, mvtFeature.id);\n    }\n}\n\n/* Change bigint to number for comparison with MVT */\nfunction convertBigIntPropertyValues(mltProperties: Record<string, any>) {\n    for (const key of Object.keys(mltProperties)) {\n        if (typeof mltProperties[key] === \"bigint\") {\n            mltProperties[key] = Number(mltProperties[key]);\n        }\n    }\n}\n\nfunction transformPropertyNames(properties: Record<string, any>) {\n    const propertyNames = Object.keys(properties);\n    for (let k = 0; k < propertyNames.length; k++) {\n        const key = propertyNames[k];\n\n        let newKey = key;\n        /* rename the property names which are separated with : in mlt to match _ in omt mvts */\n        if (key.startsWith(\"name\") && key.includes(\":\")) {\n            newKey = (key as any).replaceAll(\":\", \"_\");\n            properties[newKey] = properties[key];\n            delete properties[key];\n        }\n\n        /* Currently id is not supported as a property name in a FeatureTable,\n         *  so this quick workaround is implemented */\n        if (newKey === \"_id\") {\n            properties.id = properties[newKey];\n            delete properties[newKey];\n        }\n    }\n}\n"
  },
  {
    "path": "ts/src/mltDecoder.ts",
    "content": "import FeatureTable from \"./vector/featureTable\";\nimport { type Column, LogicalScalarType, ScalarType } from \"./metadata/tileset/tilesetMetadata\";\nimport IntWrapper from \"./decoding/intWrapper\";\nimport { decodeStreamMetadata, type RleEncodedStreamMetadata } from \"./metadata/tile/streamMetadataDecoder\";\nimport { VectorType } from \"./vector/vectorType\";\nimport { Int32FlatVector } from \"./vector/flat/int32FlatVector\";\nimport BitVector from \"./vector/flat/bitVector\";\nimport {\n    decodeUnsignedConstInt32Stream,\n    decodeUnsignedConstInt64Stream,\n    decodeUnsignedInt64AsFloat64Stream,\n    decodeUnsignedInt32Stream,\n    decodeUnsignedInt64Stream,\n    decodeSequenceInt32Stream,\n    decodeSequenceInt64Stream,\n    getVectorType,\n} from \"./decoding/integerStreamDecoder\";\nimport { Int32SequenceVector } from \"./vector/sequence/int32SequenceVector\";\nimport { Int64FlatVector } from \"./vector/flat/int64FlatVector\";\nimport { Int64SequenceVector } from \"./vector/sequence/int64SequenceVector\";\nimport type { IdVector } from \"./vector/idVector\";\nimport { decodeVarintInt32 } from \"./decoding/integerDecodingUtils\";\nimport { decodeGeometryColumn } from \"./decoding/geometryDecoder\";\nimport { decodePropertyColumn } from \"./decoding/propertyDecoder\";\nimport { Int32ConstVector } from \"./vector/constant/int32ConstVector\";\nimport { Int64ConstVector } from \"./vector/constant/int64ConstVector\";\nimport type GeometryScaling from \"./decoding/geometryScaling\";\nimport { decodeBooleanRle } from \"./decoding/decodingUtils\";\nimport { DoubleFlatVector } from \"./vector/flat/doubleFlatVector\";\nimport { decodeEmbeddedTileSetMetadata } from \"./metadata/tileset/embeddedTilesetMetadataDecoder\";\nimport { hasStreamCount, isGeometryColumn, isLogicalIdColumn } from \"./metadata/tileset/typeMap\";\nimport type { StreamMetadata } from \"./metadata/tile/streamMetadataDecoder\";\nimport type { GeometryVector } from \"./vector/geometry/geometryVector\";\nimport type Vector from \"./vector/vector\";\nimport type { GpuVector } from \"./vector/geometry/gpuVector\";\n\n/**\n * Decodes a tile with embedded metadata (Tag 0x01 format).\n * This is the primary decoder function for MLT tiles.\n *\n * @param tile The tile data to decode (will be decompressed if gzip-compressed)\n * @param geometryScaling Optional geometry scaling parameters\n * @param idWithinMaxSafeInteger If true, limits ID values to JavaScript safe integer range (53 bits)\n */\nexport default function decodeTile(\n    tile: Uint8Array,\n    geometryScaling?: GeometryScaling,\n    idWithinMaxSafeInteger = true,\n): FeatureTable[] {\n    const offset = new IntWrapper(0);\n    const featureTables: FeatureTable[] = [];\n\n    while (offset.get() < tile.length) {\n        const blockLength = decodeVarintInt32(tile, offset, 1)[0] >>> 0;\n        const blockStart = offset.get();\n        const blockEnd = blockStart + blockLength;\n        if (blockEnd > tile.length) {\n            throw new Error(`Block overruns tile: ${blockEnd} > ${tile.length}`);\n        }\n\n        const tag = decodeVarintInt32(tile, offset, 1)[0] >>> 0;\n        if (tag !== 1) {\n            // Skip unknown block types\n            offset.set(blockEnd);\n            continue;\n        }\n\n        const [metadata, extent] = decodeEmbeddedTileSetMetadata(tile, offset);\n        const featureTableMetadata = metadata.featureTables[0];\n\n        let idVector: IdVector | null = null;\n        let geometryVector: GeometryVector | GpuVector | null = null;\n        const propertyVectors: Vector[] = [];\n        let numFeatures = 0;\n\n        for (const columnMetadata of featureTableMetadata.columns) {\n            const columnName = columnMetadata.name;\n\n            if (isLogicalIdColumn(columnMetadata)) {\n                let nullabilityBuffer = null;\n                // Check column metadata nullable flag, not numStreams (ID columns don't have stream count)\n                if (columnMetadata.nullable) {\n                    const presentStreamMetadata = decodeStreamMetadata(tile, offset);\n                    const streamDataStart = offset.get();\n                    const values = decodeBooleanRle(\n                        tile,\n                        presentStreamMetadata.numValues,\n                        presentStreamMetadata.byteLength,\n                        offset,\n                    );\n                    offset.set(streamDataStart + presentStreamMetadata.byteLength);\n                    nullabilityBuffer = new BitVector(values, presentStreamMetadata.numValues);\n                }\n\n                const idDataStreamMetadata = decodeStreamMetadata(tile, offset);\n                // decompressedCount is the count WITHOUT nulls, but we may have nulls\n                numFeatures = nullabilityBuffer ? nullabilityBuffer.size() : idDataStreamMetadata.decompressedCount;\n\n                idVector = decodeIdColumn(\n                    tile,\n                    columnMetadata,\n                    offset,\n                    columnName,\n                    idDataStreamMetadata,\n                    nullabilityBuffer ?? numFeatures,\n                    idWithinMaxSafeInteger,\n                );\n            } else if (isGeometryColumn(columnMetadata)) {\n                const numStreams = decodeVarintInt32(tile, offset, 1)[0];\n\n                // If no ID column, get numFeatures from geometry type stream metadata\n                if (numFeatures === 0) {\n                    const savedOffset = offset.get();\n                    const geometryTypeMetadata = decodeStreamMetadata(tile, offset);\n                    numFeatures = geometryTypeMetadata.decompressedCount;\n                    offset.set(savedOffset); // Reset to re-read in decodeGeometryColumn\n                }\n\n                if (geometryScaling) {\n                    geometryScaling.scale = geometryScaling.extent / extent;\n                }\n\n                geometryVector = decodeGeometryColumn(tile, numStreams, offset, numFeatures, geometryScaling);\n            } else {\n                const columnHasStreamCount = hasStreamCount(columnMetadata);\n                const numStreams = columnHasStreamCount ? decodeVarintInt32(tile, offset, 1)[0] : 1;\n\n                if (numStreams === 0) {\n                    continue;\n                }\n\n                const propertyVector = decodePropertyColumn(\n                    tile,\n                    offset,\n                    columnMetadata,\n                    numStreams,\n                    numFeatures,\n                    undefined,\n                );\n                if (propertyVector) {\n                    if (Array.isArray(propertyVector)) {\n                        for (const property of propertyVector) {\n                            propertyVectors.push(property);\n                        }\n                    } else {\n                        propertyVectors.push(propertyVector);\n                    }\n                }\n            }\n        }\n\n        const featureTable = new FeatureTable(\n            featureTableMetadata.name,\n            geometryVector,\n            idVector,\n            propertyVectors,\n            extent,\n        );\n        featureTables.push(featureTable);\n        offset.set(blockEnd);\n    }\n\n    return featureTables;\n}\n\nfunction decodeIdColumn(\n    tile: Uint8Array,\n    columnMetadata: Column,\n    offset: IntWrapper,\n    columnName: string,\n    idDataStreamMetadata: StreamMetadata,\n    sizeOrNullabilityBuffer: number | BitVector,\n    idWithinMaxSafeInteger = false,\n): IdVector {\n    const scalarTypeMetadata = columnMetadata.scalarType;\n    if (\n        !scalarTypeMetadata ||\n        scalarTypeMetadata.type !== \"logicalType\" ||\n        scalarTypeMetadata.logicalType !== LogicalScalarType.ID\n    ) {\n        throw new Error(`ID column must be a logical ID scalar type: ${columnName}`);\n    }\n\n    const idDataType = scalarTypeMetadata.longID ? ScalarType.UINT_64 : ScalarType.UINT_32;\n    const nullabilityBuffer = typeof sizeOrNullabilityBuffer === \"number\" ? undefined : sizeOrNullabilityBuffer;\n\n    const vectorType = getVectorType(\n        idDataStreamMetadata,\n        sizeOrNullabilityBuffer,\n        tile,\n        offset,\n        idDataType === ScalarType.UINT_64 ? \"int64\" : \"int32\",\n    );\n    if (idDataType === ScalarType.UINT_32) {\n        switch (vectorType) {\n            case VectorType.FLAT: {\n                const id = decodeUnsignedInt32Stream(tile, offset, idDataStreamMetadata, undefined, nullabilityBuffer);\n                return new Int32FlatVector(columnName, id, sizeOrNullabilityBuffer);\n            }\n            case VectorType.SEQUENCE: {\n                const id = decodeSequenceInt32Stream(tile, offset, idDataStreamMetadata);\n                return new Int32SequenceVector(\n                    columnName,\n                    id[0],\n                    id[1],\n                    (idDataStreamMetadata as RleEncodedStreamMetadata).numRleValues,\n                );\n            }\n            case VectorType.CONST: {\n                const id = decodeUnsignedConstInt32Stream(tile, offset, idDataStreamMetadata);\n                return new Int32ConstVector(columnName, id, sizeOrNullabilityBuffer, false);\n            }\n        }\n    }\n    switch (vectorType) {\n        case VectorType.FLAT: {\n            if (idWithinMaxSafeInteger) {\n                const id = decodeUnsignedInt64AsFloat64Stream(tile, offset, idDataStreamMetadata);\n                return new DoubleFlatVector(columnName, id, sizeOrNullabilityBuffer);\n            }\n            const id = decodeUnsignedInt64Stream(tile, offset, idDataStreamMetadata, nullabilityBuffer);\n            return new Int64FlatVector(columnName, id, sizeOrNullabilityBuffer);\n        }\n        case VectorType.SEQUENCE: {\n            const id = decodeSequenceInt64Stream(tile, offset, idDataStreamMetadata);\n            return new Int64SequenceVector(\n                columnName,\n                id[0],\n                id[1],\n                (idDataStreamMetadata as RleEncodedStreamMetadata).numRleValues,\n            );\n        }\n        case VectorType.CONST: {\n            const id = decodeUnsignedConstInt64Stream(tile, offset, idDataStreamMetadata);\n            return new Int64ConstVector(columnName, id, sizeOrNullabilityBuffer, false);\n        }\n    }\n\n    throw new Error(\"Vector type not supported for id column.\");\n}\n"
  },
  {
    "path": "ts/src/mltMetadata.ts",
    "content": "export enum ColumnDataType {\n    STRING = 0,\n    FLOAT = 1,\n    DOUBLE = 2,\n    INT_64 = 3,\n    UINT_64 = 4,\n    BOOLEAN = 5,\n    GEOMETRY = 6,\n    GEOMETRY_M = 7,\n    GEOMETRY_Z = 8,\n    GEOMETRY_ZM = 9,\n}\n\nexport enum ColumnEncoding {\n    /*\n     * String -> no dictionary coding\n     * Geometry -> standard unsorted encoding\n     * */\n    PLAIN = 0,\n    VARINT = 1,\n    DELTA_VARINT = 2,\n    RLE = 3,\n    BOOLEAN_RLE = 4,\n    BYTE_RLE = 5,\n    DICTIONARY = 6,\n    LOCALIZED_DICTIONARY = 7,\n    ORDERED_GEOMETRY_ENCODING = 8,\n    INDEXED_COORDINATE_ENCODING = 9,\n}\n\nexport interface ColumnMetadata {\n    columnName: string;\n    columnType: ColumnDataType;\n    columnEncoding: ColumnEncoding;\n    streams: Map<string, StreamMetadata>;\n}\n\nexport interface StreamMetadata {\n    numValues: number;\n    byteLength: number;\n}\n\nexport interface LayerMetadata {\n    name: string;\n    numColumns: number;\n    numFeatures: number;\n    columnMetadata: ColumnMetadata[];\n}\n"
  },
  {
    "path": "ts/src/synthetic.spec.ts",
    "content": "import { readFile } from \"node:fs/promises\";\nimport { describe, expect, it } from \"vitest\";\nimport { classifyRings } from \"@maplibre/maplibre-gl-style-spec\";\nimport { GEOMETRY_TYPE } from \"./vector/geometry/geometryType\";\nimport { compareWithTolerance, getTestCases, writeActualOutput } from \"../../test/synthetic/synthetic-test-utils\";\nimport decodeTile from \"./mltDecoder\";\nimport type { Geometry } from \"./vector/geometry/geometryVector\";\nimport type FeatureTable from \"./vector/featureTable\";\n\nconst EARCUT_MAX_RINGS = 500;\n\nconst UNIMPLEMENTED_SYNTHETICS: string[] = [\n    \"0x01/multipoint_morton\",\n    \"0x01/poly_morton_hole_morton\",\n    \"0x01/poly_multi_morton_hole_morton\",\n    \"0x01/poly_multi_morton_ring_morton\",\n    \"0x01/poly_multi_morton_ring_no_morton\",\n];\n\ndescribe(\"MLT Decoder - Synthetic tests\", () => {\n    expect.addEqualityTesters([compareWithTolerance]);\n    const testCases = getTestCases(UNIMPLEMENTED_SYNTHETICS);\n    for (const { name, content, fileName } of testCases.active) {\n        it(name, async () => {\n            const actual = await decodeMLT(fileName);\n            writeActualOutput(fileName, actual);\n            expect(actual).toEqual(content);\n        });\n    }\n\n    for (const skippedTest of testCases.skipped) {\n        it.skip(skippedTest, () => {\n            // Test is skipped since it is not supported yet\n        });\n    }\n});\n\nasync function decodeMLT(mltFilePath: string) {\n    const mltBuffer = await readFile(mltFilePath);\n    const featureTables = decodeTile(mltBuffer, undefined, false);\n    return featureTablesToFeatureCollection(featureTables) as unknown as Record<string, unknown>;\n}\n\nfunction featureTablesToFeatureCollection(featureTables: FeatureTable[]): GeoJSON.FeatureCollection {\n    const features: GeoJSON.Feature[] = [];\n    for (const table of featureTables) {\n        for (const feature of table.getFeatures()) {\n            const geojsonFeature: GeoJSON.Feature = {\n                type: \"Feature\",\n                geometry: getGeometry(feature.geometry),\n                properties: {\n                    _layer: table.name,\n                    _extent: table.extent,\n                    ...Object.fromEntries(Object.entries(feature.properties).map(([k, v]) => [k, safeNumber(v)])),\n                },\n            };\n            const safeId = safeNumber(feature.id);\n            if (safeId !== null && safeId !== undefined) {\n                geojsonFeature.id = safeId;\n            }\n            features.push(geojsonFeature);\n        }\n    }\n    return { type: \"FeatureCollection\", features };\n}\n\nfunction safeNumber<T>(val: bigint | T): T | number {\n    return typeof val === \"bigint\" ? Number(val) : val;\n}\n\nfunction getGeometry(geometry: Geometry): GeoJSON.Geometry {\n    const coords = geometry.coordinates.map((ring) => ring.map((p) => [p.x, p.y]));\n    switch (geometry.type) {\n        case GEOMETRY_TYPE.POINT:\n            return { type: \"Point\", coordinates: coords[0][0] };\n        case GEOMETRY_TYPE.LINESTRING:\n            return { type: \"LineString\", coordinates: coords[0] };\n        case GEOMETRY_TYPE.POLYGON:\n            return { type: \"Polygon\", coordinates: coords };\n        case GEOMETRY_TYPE.MULTIPOINT:\n            return { type: \"MultiPoint\", coordinates: coords.map((r) => r[0]) };\n        case GEOMETRY_TYPE.MULTILINESTRING:\n            return { type: \"MultiLineString\", coordinates: coords };\n        case GEOMETRY_TYPE.MULTIPOLYGON: {\n            const polygons = classifyRings(geometry.coordinates, EARCUT_MAX_RINGS);\n            return {\n                type: \"MultiPolygon\",\n                coordinates: polygons.map((polygon) => polygon.map((ring) => ring.map((p) => [p.x, p.y]))),\n            };\n        }\n        default:\n            throw new Error(`Unsupported geometry type: ${geometry.type}`);\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/constant/int32ConstVector.ts",
    "content": "import type BitVector from \"../flat/bitVector\";\nimport Vector from \"../vector\";\n\nexport class Int32ConstVector extends Vector<Int32Array | Uint32Array, number> {\n    public constructor(name: string, value: number, sizeOrNullabilityBuffer: number | BitVector, isSigned: boolean) {\n        super(name, isSigned ? Int32Array.of(value) : Uint32Array.of(value), sizeOrNullabilityBuffer);\n    }\n\n    protected getValueFromBuffer(_index: number): number {\n        return this.dataBuffer[0];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/constant/int64ConstVector.ts",
    "content": "import type BitVector from \"../flat/bitVector\";\nimport Vector from \"../vector\";\n\nexport class Int64ConstVector extends Vector<BigInt64Array | BigUint64Array, bigint> {\n    public constructor(name: string, value: bigint, sizeOrNullabilityBuffer: number | BitVector, isSigned: boolean) {\n        super(name, isSigned ? BigInt64Array.of(value) : BigUint64Array.of(value), sizeOrNullabilityBuffer);\n    }\n\n    protected getValueFromBuffer(_index: number): bigint {\n        return this.dataBuffer[0];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/dictionary/stringDictionaryVector.ts",
    "content": "import { VariableSizeVector } from \"../variableSizeVector\";\nimport type BitVector from \"../flat/bitVector\";\nimport { decodeString } from \"../../decoding/decodingUtils\";\n\nexport class StringDictionaryVector extends VariableSizeVector<Uint8Array, string> {\n    constructor(\n        name: string,\n        private readonly indexBuffer: Uint32Array,\n        offsetBuffer: Uint32Array,\n        dictionaryBuffer: Uint8Array,\n        nullabilityBuffer?: BitVector,\n    ) {\n        super(name, offsetBuffer, dictionaryBuffer, nullabilityBuffer ?? indexBuffer.length);\n        this.indexBuffer = indexBuffer;\n    }\n\n    protected getValueFromBuffer(index: number): string {\n        const offset = this.indexBuffer[index];\n        const start = this.offsetBuffer[offset];\n        const end = this.offsetBuffer[offset + 1];\n        return decodeString(this.dataBuffer, start, end);\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/featureTable.ts",
    "content": "import type { Geometry, GeometryVector } from \"./geometry/geometryVector\";\nimport type Vector from \"./vector\";\nimport type { IdVector } from \"./idVector\";\nimport { Int32FlatVector } from \"./flat/int32FlatVector\";\nimport { DoubleFlatVector } from \"./flat/doubleFlatVector\";\nimport { Int32SequenceVector } from \"./sequence/int32SequenceVector\";\nimport { Int32ConstVector } from \"./constant/int32ConstVector\";\nimport type { GpuVector } from \"./geometry/gpuVector\";\n\nexport interface Feature {\n    id: number | bigint;\n    geometry: Geometry;\n    properties: { [key: string]: unknown };\n}\n\nexport default class FeatureTable {\n    private propertyVectorsMap: Map<string, Vector>;\n\n    constructor(\n        private readonly _name: string,\n        private readonly _geometryVector: GeometryVector | GpuVector,\n        private readonly _idVector?: IdVector,\n        private readonly _propertyVectors?: Vector[],\n        private readonly _extent = 4096,\n    ) {}\n\n    get name(): string {\n        return this._name;\n    }\n\n    get idVector(): IdVector {\n        return this._idVector;\n    }\n\n    get geometryVector(): GeometryVector | GpuVector {\n        return this._geometryVector;\n    }\n\n    get propertyVectors(): Vector[] {\n        return this._propertyVectors;\n    }\n\n    getPropertyVector(name: string): Vector {\n        if (!this.propertyVectorsMap) {\n            this.propertyVectorsMap = new Map(this._propertyVectors.map((vector) => [vector.name, vector]));\n        }\n\n        return this.propertyVectorsMap.get(name);\n    }\n\n    get numFeatures(): number {\n        return this.geometryVector.numGeometries;\n    }\n\n    get extent(): number {\n        return this._extent;\n    }\n\n    /**\n     * Returns all features as an array\n     */\n    getFeatures(): Feature[] {\n        const features: Feature[] = [];\n        const geometries = this.geometryVector.getGeometries();\n\n        for (let i = 0; i < this.numFeatures; i++) {\n            let id;\n            if (this.idVector) {\n                const idValue = this.idVector.getValue(i);\n                id = this.containsMaxSafeIntegerValues(this.idVector) && idValue !== null ? Number(idValue) : idValue;\n            }\n            const geometry = {\n                coordinates: geometries[i],\n                type: this.geometryVector.geometryType(i),\n            };\n\n            const properties: { [key: string]: unknown } = {};\n            for (const propertyColumn of this.propertyVectors) {\n                if (!propertyColumn) continue;\n                const columnName = propertyColumn.name;\n                const propertyValue = propertyColumn.getValue(i);\n                if (propertyValue !== null) {\n                    properties[columnName] = propertyValue;\n                }\n            }\n\n            features.push({ id, geometry, properties });\n        }\n        return features;\n    }\n\n    private containsMaxSafeIntegerValues(idVector: IdVector) {\n        return (\n            idVector instanceof Int32FlatVector ||\n            idVector instanceof Int32ConstVector ||\n            idVector instanceof Int32SequenceVector ||\n            idVector instanceof DoubleFlatVector\n        );\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/filter/flatSelectionVector.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { FlatSelectionVector } from \"./flatSelectionVector\";\n\ndescribe(\"flatSelectionVector\", () => {\n    describe(\"Basic functionality\", () => {\n        it(\"Should store and retrieve indices\", () => {\n            const fsVector = new FlatSelectionVector([0, 1, 999999999999, -28, 36]);\n            expect(fsVector.getIndex(0)).toBe(0);\n            expect(fsVector.getIndex(2)).toBe(999999999999);\n            expect(fsVector.getIndex(3)).toBe(-28);\n\n            fsVector.setIndex(2, -48);\n            expect(fsVector.getIndex(2)).toBe(-48);\n        });\n\n        it(\"Should throw RangeError for out of bounds access\", () => {\n            const fsVector = new FlatSelectionVector([0, 1, 2]);\n            expect(() => fsVector.getIndex(10)).toThrowError(\"Index out of bounds\");\n            expect(() => fsVector.getIndex(-1)).toThrowError(\"Index out of bounds\");\n            expect(() => fsVector.setIndex(-1, 0)).toThrowError(\"Index out of bounds\");\n            expect(() => fsVector.setIndex(10, 0)).toThrowError(\"Index out of bounds\");\n        });\n    });\n\n    describe(\"Array wrapper behavior\", () => {\n        it(\"Should return reference to underlying array\", () => {\n            const vector = [0, 1, 2, 3, 4];\n            const fsVector = new FlatSelectionVector(vector);\n            expect(fsVector.selectionValues()).toBe(vector);\n        });\n\n        it(\"Should use array length as default limit and capacity\", () => {\n            const fsVector = new FlatSelectionVector([1, 2, 3, 4, 5]);\n            expect(fsVector.limit).toBe(5);\n            expect(fsVector.capacity).toBe(5);\n        });\n\n        it(\"Should allow custom limit independent of array length\", () => {\n            const fsVector = new FlatSelectionVector([1, 2, 3, 4, 5], 3);\n            expect(fsVector.limit).toBe(3);\n            expect(fsVector.capacity).toBe(5);\n        });\n    });\n    describe(\"set Limit Tests\", () => {\n        it(\"should set Limit\", () => {\n            const fsVector = new FlatSelectionVector([1, 2, 3, 4, 5], 3);\n            fsVector.setLimit(2);\n            expect(fsVector.limit).toBe(2);\n        });\n        it(\"should throw out of bounds error\", () => {\n            const fsVector = new FlatSelectionVector([1, 2, 3, 4, 5], 3);\n            expect(() => fsVector.setLimit(-10)).toThrowError(\"Limit out of bounds\");\n            expect(() => fsVector.setLimit(10)).toThrowError(\"Limit out of bounds\");\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/filter/flatSelectionVector.ts",
    "content": "import type { SelectionVector } from \"./selectionVector\";\n\n/**\n * Array-based SelectionVector for non-sequential selections.\n * Stores indices explicitly, suitable for irregular patterns and frequent modifications.\n */\nexport class FlatSelectionVector implements SelectionVector {\n    /**\n     * @param _selectionVector\n     * @param _limit In write mode the limit of a Buffer is the limit of how much data you can write into the buffer.\n     * In write mode the limit is equal to the capacity of the Buffer.\n     */\n    constructor(\n        private _selectionVector: number[],\n        private _limit?: number,\n    ) {\n        if (!this._limit) {\n            this._limit = this._selectionVector.length;\n        }\n    }\n\n    /** @inheritdoc */\n    getIndex(index: number): number {\n        if (index >= this._limit || index < 0) {\n            throw new RangeError(\"Index out of bounds\");\n        }\n\n        return this._selectionVector[index];\n    }\n\n    /** @inheritdoc */\n    setIndex(index: number, value: number): void {\n        if (index >= this._limit || index < 0) {\n            throw new RangeError(\"Index out of bounds\");\n        }\n\n        this._selectionVector[index] = value;\n    }\n\n    /** @inheritdoc */\n    setLimit(limit: number): void {\n        if (limit < 0 || limit > this.capacity) {\n            throw new RangeError(\"Limit out of bounds\");\n        }\n        this._limit = limit;\n    }\n\n    /** @inheritdoc */\n    selectionValues(): number[] {\n        return this._selectionVector;\n    }\n\n    /** @inheritdoc */\n    get capacity() {\n        return this._selectionVector.length;\n    }\n\n    /** @inheritdoc */\n    get limit() {\n        return this._limit;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/filter/selectionVector.ts",
    "content": "export interface SelectionVector {\n    getIndex(index: number): number;\n    setIndex(index: number, value: number): void;\n    setLimit(limit: number): void;\n    selectionValues(): number[];\n    /* Index of the first element that should not be read or written.\n     * It's not the last index that can be accessed, but rather the index that marks the end of\n     * the valid data in the buffer */\n    get limit();\n    /* Total size of the buffer */\n    get capacity();\n}\n"
  },
  {
    "path": "ts/src/vector/filter/selectionVectorUtil.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport {\n    createSelectionVector,\n    createNullableSelectionVector,\n    updateNullableSelectionVector,\n} from \"./selectionVectorUtils\";\nimport { FlatSelectionVector } from \"./flatSelectionVector\";\nimport { SequenceSelectionVector } from \"./sequenceSelectionVector\";\nimport BitVector from \"../flat/bitVector\";\n\ndescribe(\"selectionVectorUtils\", () => {\n    describe(\"createSelectionVector\", () => {\n        it(\"Should create a SequenceSelectionVector with given size\", () => {\n            const sv = createSelectionVector(5);\n            expect(sv).toBeInstanceOf(SequenceSelectionVector);\n            expect(sv.limit).toBe(5);\n        });\n\n        it(\"Should handle zero size\", () => {\n            const sv = createSelectionVector(0);\n            expect(sv).toBeInstanceOf(SequenceSelectionVector);\n            expect(sv.limit).toBe(0);\n        });\n    });\n\n    describe(\"createNullableSelectionVector\", () => {\n        it(\"Should return all indices when nullabilityBuffer is not provided\", () => {\n            const sv = createNullableSelectionVector(5);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(5);\n        });\n\n        it(\"Should return empty vector when size is 0 and nullabilityBuffer is not provided\", () => {\n            const sv = createNullableSelectionVector(0);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(0);\n        });\n\n        it(\"Should return empty vector when size is 0 with BitVector provided\", () => {\n            const buffer = new Uint8Array([0b11111111]);\n            const bitVector = new BitVector(buffer, 8);\n            const sv = createNullableSelectionVector(0, bitVector);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(0);\n        });\n\n        it(\"Should create FlatSelectionVector with only set bits\", () => {\n            const buffer = new Uint8Array([0b00001011]); // bits 0, 1, 3 are set\n            const bitVector = new BitVector(buffer, 8);\n            const sv = createNullableSelectionVector(8, bitVector);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(3);\n        });\n\n        it(\"Should create empty vector when no bits are set\", () => {\n            const buffer = new Uint8Array([0b00000000]);\n            const bitVector = new BitVector(buffer, 8);\n            const sv = createNullableSelectionVector(8, bitVector);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(0);\n        });\n\n        it(\"Should handle multiple bytes in BitVector\", () => {\n            const buffer = new Uint8Array([0b10101010, 0b01010101]);\n            const bitVector = new BitVector(buffer, 16);\n            const sv = createNullableSelectionVector(16, bitVector);\n            expect(sv).toBeInstanceOf(FlatSelectionVector);\n            expect(sv.limit).toBe(8);\n        });\n    });\n\n    describe(\"updateNullableSelectionVector\", () => {\n        describe(\"with FlatSelectionVector\", () => {\n            it(\"Should return new instance when filtering with BitVector\", () => {\n                const selectionVector = new FlatSelectionVector([0, 1, 2, 3, 4, 5, 6, 7]);\n                const buffer = new Uint8Array([0b00001011]);\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(3);\n                expect(result).not.toBe(selectionVector);\n            });\n\n            it(\"Should return same instance when BitVector is null\", () => {\n                const selectionVector = new FlatSelectionVector([0, 1, 2, 3, 4, 5, 6, 7]);\n                const result = updateNullableSelectionVector(selectionVector, null);\n                expect(result).toStrictEqual(selectionVector);\n            });\n\n            it(\"Should return all indices when nullabilityBuffer is undefined\", () => {\n                const selectionVector = new FlatSelectionVector([0, 2, 4, 6]);\n                const result = updateNullableSelectionVector(selectionVector, undefined);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(4);\n            });\n\n            it(\"Should keep all indices when all bits are set\", () => {\n                const selectionVector = new FlatSelectionVector([0, 2, 4, 6]);\n                const buffer = new Uint8Array([0b01010101]);\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(4);\n            });\n\n            it(\"Should filter out null indices from selection vector\", () => {\n                const selectionVector = new FlatSelectionVector([0, 2, 4, 6]);\n                const buffer = new Uint8Array([0b01000101]); // bits at 0, 2, 6\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(3);\n            });\n\n            it(\"Should return empty vector when all selected indices are null\", () => {\n                const selectionVector = new FlatSelectionVector([1, 3, 5]);\n                const buffer = new Uint8Array([0b01010101]); // bits at 0, 2, 4, 6 (not 1, 3, 5)\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(0);\n            });\n\n            it(\"Should handle empty FlatSelectionVector\", () => {\n                const selectionVector = new FlatSelectionVector([]);\n                const buffer = new Uint8Array([0b11111111]);\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(0);\n            });\n\n            it(\"Should filter large index values from selection vector\", () => {\n                const selectionVector = new FlatSelectionVector([0, 8, 16]);\n                const buffer = new Uint8Array([0b00000001, 0b00000000, 0b00000000]);\n                const bitVector = new BitVector(buffer, 24);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(1);\n            });\n        });\n\n        describe(\"with SequenceSelectionVector\", () => {\n            it(\"Should filter SequenceSelectionVector with all bits set\", () => {\n                const selectionVector = new SequenceSelectionVector(0, 2, 4); // [0, 2, 4, 6]\n                const buffer = new Uint8Array([0b01010101]);\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(4);\n            });\n\n            it(\"Should partially filter SequenceSelectionVector\", () => {\n                const selectionVector = new SequenceSelectionVector(0, 2, 4); // [0, 2, 4, 6]\n                const buffer = new Uint8Array([0b00010001]); // bits at 0, 4\n                const bitVector = new BitVector(buffer, 8);\n                const result = updateNullableSelectionVector(selectionVector, bitVector);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(2);\n            });\n\n            it(\"Should preserve all SequenceSelectionVector values when nullabilityBuffer is undefined\", () => {\n                const selectionVector = new SequenceSelectionVector(1, 3, 3); // [1, 4, 7]\n                const result = updateNullableSelectionVector(selectionVector, undefined);\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(3);\n            });\n\n            it(\"Should create FlatSelectionVector when BitVector is null\", () => {\n                const selectionVector = new SequenceSelectionVector(0, 2, 4); // [0, 2, 4, 6]\n                const result = updateNullableSelectionVector(selectionVector, null);\n\n                expect(result).toBeInstanceOf(FlatSelectionVector);\n                expect(result.limit).toBe(4);\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/filter/selectionVectorUtils.ts",
    "content": "import type { SelectionVector } from \"./selectionVector\";\nimport { FlatSelectionVector } from \"./flatSelectionVector\";\nimport type BitVector from \"../flat/bitVector\";\nimport { SequenceSelectionVector } from \"./sequenceSelectionVector\";\n\nexport function createSelectionVector(size: number) {\n    return new SequenceSelectionVector(0, 1, size);\n}\n\n/**\n * Creates a selection vector containing indices of non-null values.\n * @param size - The total number of elements to consider\n * @param nullabilityBuffer - Optional bit vector where 1=not null, 0=null. If undefined/null, all values are considered non-null.\n */\nexport function createNullableSelectionVector(size: number, nullabilityBuffer?: BitVector): SelectionVector {\n    const selectionVector = [];\n    for (let i = 0; i < size; i++) {\n        // Include index if no nullability buffer (all non-null) OR if bit is set (non-null)\n        if (!nullabilityBuffer || nullabilityBuffer.get(i)) {\n            selectionVector.push(i);\n        }\n    }\n    return new FlatSelectionVector(selectionVector);\n}\n\n/**\n * Filters an existing selection vector to include only non-null values.\n * @param selectionVector - The input selection vector to filter\n * @param nullabilityBuffer - Optional bit vector where 1=not null, 0=null. If undefined/null, all values are considered non-null.\n */\nexport function updateNullableSelectionVector(\n    selectionVector: SelectionVector,\n    nullabilityBuffer?: BitVector,\n): SelectionVector {\n    const filteredIndices = [];\n    for (let i = 0; i < selectionVector.limit; i++) {\n        const vectorIndex = selectionVector.getIndex(i);\n        // Include index if no nullability buffer (all non-null) OR if bit is set (non-null)\n        if (!nullabilityBuffer || nullabilityBuffer.get(vectorIndex)) {\n            filteredIndices.push(vectorIndex);\n        }\n    }\n    return new FlatSelectionVector(filteredIndices);\n}\n"
  },
  {
    "path": "ts/src/vector/filter/sequenceSelectionVector.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { SequenceSelectionVector } from \"./sequenceSelectionVector\";\n\ndescribe(\"sequenceSelectionVector\", () => {\n    describe(\"getIndex Test\", () => {\n        it(\"Should return sequential values starting from 0\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(vector.getIndex(0)).toBe(0);\n            expect(vector.getIndex(2)).toBe(2);\n            expect(vector.getIndex(4)).toBe(4);\n        });\n\n        it(\"Should calculate values with custom base and delta\", () => {\n            const vector = new SequenceSelectionVector(10, 5, 5);\n            expect(vector.getIndex(0)).toBe(10);\n            expect(vector.getIndex(1)).toBe(15);\n            expect(vector.getIndex(2)).toBe(20);\n        });\n\n        it(\"Should calculate values with negative delta\", () => {\n            const vector = new SequenceSelectionVector(100, -10, 5);\n            expect(vector.getIndex(0)).toBe(100);\n            expect(vector.getIndex(1)).toBe(90);\n            expect(vector.getIndex(2)).toBe(80);\n        });\n\n        it(\"Should throw RangeError for out of bounds indices\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(() => vector.getIndex(80)).toThrowError(\"Index out of bounds\");\n            expect(() => vector.getIndex(-36)).toThrowError(\"Index out of bounds\");\n        });\n    });\n\n    describe(\"setIndex Test\", () => {\n        it(\"Should update value at specified index\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            vector.setIndex(0, 25);\n            vector.setIndex(2, -48);\n            vector.setIndex(3, 1000000000000001);\n\n            expect(vector.getIndex(0)).toBe(25);\n            expect(vector.getIndex(2)).toBe(-48);\n            expect(vector.getIndex(3)).toBe(1000000000000001);\n        });\n\n        it(\"Should throw RangeError for out of bounds indices\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(() => vector.setIndex(-1, 0)).toThrowError(\"Index out of bounds\");\n            expect(() => vector.setIndex(25, 52)).toThrowError(\"Index out of bounds\");\n        });\n    });\n\n    describe(\"limit Test\", () => {\n        it(\"Should initialize limit to size\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(vector.limit).toBe(5);\n\n            const emptyVector = new SequenceSelectionVector(0, 1, 0);\n            expect(emptyVector.limit).toBe(0);\n        });\n\n        it(\"Should update limit independently of capacity\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 10);\n            expect(vector.capacity).toBe(10);\n\n            vector.setLimit(3);\n            expect(vector.limit).toBe(3);\n            expect(vector.capacity).toBe(10);\n\n            vector.setLimit(8);\n            expect(vector.limit).toBe(8);\n            expect(vector.capacity).toBe(10);\n        });\n\n        it(\"Should throw RangeError for negative limit\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(() => vector.setLimit(-1)).toThrowError(\"Limit out of bounds\");\n            expect(() => vector.setLimit(100)).toThrowError(\"Limit out of bounds\");\n        });\n\n        it(\"Should allow setting limit to 0\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            vector.setLimit(0);\n            expect(vector.limit).toBe(0);\n        });\n    });\n\n    describe(\"selectionValues Test\", () => {\n        it(\"Should return array with sequential values\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            const values = vector.selectionValues();\n            expect(values).toStrictEqual([0, 1, 2, 3, 4]);\n        });\n\n        it(\"Should return empty array for empty vector\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 0);\n            expect(vector.selectionValues()).toStrictEqual([]);\n        });\n\n        it(\"Should return array with custom base and delta\", () => {\n            const vector = new SequenceSelectionVector(10, 5, 5);\n            expect(vector.selectionValues()).toStrictEqual([10, 15, 20, 25, 30]);\n        });\n\n        it(\"Should return array with negative delta\", () => {\n            const vector = new SequenceSelectionVector(100, -10, 5);\n            expect(vector.selectionValues()).toStrictEqual([100, 90, 80, 70, 60]);\n        });\n\n        it(\"Should reflect modified values\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 3);\n            vector.setIndex(2, 999);\n            const values = vector.selectionValues();\n            expect(values).toStrictEqual([0, 1, 999]);\n        });\n    });\n\n    describe(\"capacity Test\", () => {\n        it(\"Should return capacity equal to size\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 5);\n            expect(vector.capacity).toBe(5);\n        });\n\n        it(\"Should return 0 for empty vector\", () => {\n            const vector = new SequenceSelectionVector(0, 1, 0);\n            expect(vector.capacity).toBe(0);\n        });\n\n        it(\"Should remain constant when limit changes\", () => {\n            const vector = new SequenceSelectionVector(10, 5, 50);\n            expect(vector.capacity).toBe(50);\n\n            vector.setLimit(25);\n            expect(vector.capacity).toBe(50);\n        });\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/filter/sequenceSelectionVector.ts",
    "content": "import type { SelectionVector } from \"./selectionVector\";\n\n/**\n * Memory-efficient SelectionVector for arithmetic sequences (base + index * delta).\n * Calculates values on-demand, only materializes when modified.\n */\nexport class SequenceSelectionVector implements SelectionVector {\n    private _materializedArray: number[] | null = null;\n\n    constructor(\n        private readonly _baseValue: number,\n        private readonly _delta: number,\n        private _limit: number,\n        private readonly _capacity: number = _limit,\n    ) {}\n\n    /** @inheritdoc */\n    get limit(): number {\n        return this._limit;\n    }\n\n    /** @inheritdoc */\n    get capacity(): number {\n        return this._capacity;\n    }\n\n    /** @inheritdoc */\n    selectionValues(): number[] {\n        if (!this._materializedArray) {\n            this._materializedArray = this.materialize();\n        }\n        return this._materializedArray;\n    }\n\n    private materialize(): number[] {\n        const arr = new Array<number>(this._capacity);\n        for (let i = 0; i < this._capacity; i++) {\n            arr[i] = this._baseValue + i * this._delta;\n        }\n        return arr;\n    }\n\n    /** @inheritdoc */\n    getIndex(index: number): number {\n        if (index >= this._limit || index < 0) {\n            throw new RangeError(\"Index out of bounds\");\n        }\n        if (this._materializedArray) {\n            return this._materializedArray[index];\n        }\n        return this._baseValue + index * this._delta;\n    }\n\n    /** @inheritdoc */\n    setIndex(index: number, value: number): void {\n        if (index >= this._limit || index < 0) {\n            throw new RangeError(\"Index out of bounds\");\n        }\n        if (!this._materializedArray) {\n            this._materializedArray = this.materialize();\n        }\n        this._materializedArray[index] = value;\n    }\n\n    /** @inheritdoc */\n    setLimit(limit: number): void {\n        if (limit < 0 || limit > this.capacity) {\n            throw new RangeError(\"Limit out of bounds\");\n        }\n        this._limit = limit;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/fixedSizeVector.ts",
    "content": "import Vector from \"./vector\";\n\nexport abstract class FixedSizeVector<T extends ArrayBufferView, K> extends Vector<T, K> {}\n"
  },
  {
    "path": "ts/src/vector/flat/bitVector.ts",
    "content": "export default class BitVector {\n    private readonly values: Uint8Array;\n    private readonly _size: number;\n\n    /**\n     * @param values The byte buffer containing the bit values in least-significant bit (LSB)\n     *     numbering\n     */\n    constructor(values: Uint8Array, size: number) {\n        this.values = values;\n        this._size = size;\n    }\n\n    get(index: number): boolean {\n        const byteIndex = Math.floor(index / 8);\n        const bitIndex = index % 8;\n        const b = this.values[byteIndex];\n        return ((b >> bitIndex) & 1) === 1;\n    }\n\n    set(index: number, value: boolean): void {\n        //TODO: refactor -> improve quick and dirty solution\n        const byteIndex = Math.floor(index / 8);\n        const bitIndex = index % 8;\n        this.values[byteIndex] = this.values[byteIndex] | ((value ? 1 : 0) << bitIndex);\n    }\n\n    getInt(index: number): number {\n        const byteIndex = Math.floor(index / 8);\n        const bitIndex = index % 8;\n        const b = this.values[byteIndex];\n        return (b >> bitIndex) & 1;\n    }\n\n    size(): number {\n        return this._size;\n    }\n\n    getBuffer(): Uint8Array {\n        return this.values;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/booleanFlatVector.ts",
    "content": "import type BitVector from \"./bitVector\";\nimport Vector from \"../vector\";\n\nexport class BooleanFlatVector extends Vector<Uint8Array, boolean> {\n    private readonly dataVector: BitVector;\n\n    constructor(name: string, dataVector: BitVector, sizeOrNullabilityBuffer: number | BitVector) {\n        super(name, dataVector.getBuffer(), sizeOrNullabilityBuffer);\n        this.dataVector = dataVector;\n    }\n\n    protected getValueFromBuffer(index: number): boolean {\n        return this.dataVector.get(index);\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/doubleFlatVector.ts",
    "content": "import { FixedSizeVector } from \"../fixedSizeVector\";\n\nexport class DoubleFlatVector extends FixedSizeVector<Float64Array, number> {\n    protected getValueFromBuffer(index: number): number {\n        return this.dataBuffer[index];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/floatFlatVector.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { FloatFlatVector } from \"./floatFlatVector\";\n\ndescribe(\"FloatFlatVector\", () => {\n    it(\"should construct and return values correctly\", () => {\n        const data = new Float32Array([1.5, 2.5, 3.5, 4.5, 5.5]);\n        const vec = new FloatFlatVector(\"test\", data, data.length);\n\n        expect(vec.name).toBe(\"test\");\n        expect(vec.size).toBe(5);\n        expect(vec.getValue(0)).toBe(1.5);\n        expect(vec.getValue(2)).toBe(3.5);\n        expect(vec.getValue(4)).toBe(5.5);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/flat/floatFlatVector.ts",
    "content": "import { FixedSizeVector } from \"../fixedSizeVector\";\n\nexport class FloatFlatVector extends FixedSizeVector<Float32Array, number> {\n    protected getValueFromBuffer(index: number): number {\n        return this.dataBuffer[index];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/int32FlatVector.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { Int32FlatVector } from \"./int32FlatVector\";\n\ndescribe(\"Int32FlatVector\", () => {\n    it(\"should construct and return values correctly\", () => {\n        const data = new Int32Array([10, 20, 30, 40, 50]);\n        const vec = new Int32FlatVector(\"test\", data, data.length);\n\n        expect(vec.name).toBe(\"test\");\n        expect(vec.size).toBe(5);\n        expect(vec.has(0)).toBe(true);\n        expect(vec.getValue(0)).toBe(10);\n        expect(vec.getValue(2)).toBe(30);\n        expect(vec.getValue(4)).toBe(50);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/flat/int32FlatVector.ts",
    "content": "import { FixedSizeVector } from \"../fixedSizeVector\";\n\nexport class Int32FlatVector extends FixedSizeVector<Int32Array | Uint32Array, number> {\n    protected getValueFromBuffer(index: number): number {\n        return this.dataBuffer[index];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/int64FlatVector.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { Int64FlatVector } from \"./int64FlatVector\";\n\ndescribe(\"Int64FlatVector\", () => {\n    it(\"should construct and return values correctly\", () => {\n        const data = new BigInt64Array([10n, 20n, 30n, 40n, 50n]);\n        const vec = new Int64FlatVector(\"test\", data, data.length);\n\n        expect(vec.name).toBe(\"test\");\n        expect(vec.size).toBe(5);\n        expect(vec.getValue(0)).toBe(10n);\n        expect(vec.getValue(2)).toBe(30n);\n        expect(vec.getValue(4)).toBe(50n);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/flat/int64FlatVector.ts",
    "content": "import { FixedSizeVector } from \"../fixedSizeVector\";\n\nexport class Int64FlatVector extends FixedSizeVector<BigInt64Array | BigUint64Array, bigint> {\n    protected getValueFromBuffer(index: number): bigint {\n        return this.dataBuffer[index];\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/flat/stringFlatVector.ts",
    "content": "import { VariableSizeVector } from \"../variableSizeVector\";\nimport type BitVector from \"./bitVector\";\nimport { decodeString } from \"../../decoding/decodingUtils\";\n\nexport class StringFlatVector extends VariableSizeVector<Uint8Array, string> {\n    constructor(name: string, offsetBuffer: Uint32Array, dataBuffer: Uint8Array, nullabilityBuffer?: BitVector) {\n        super(name, offsetBuffer, dataBuffer, nullabilityBuffer ?? offsetBuffer.length - 1);\n    }\n\n    protected getValueFromBuffer(index: number): string {\n        const start = this.offsetBuffer[index];\n        const end = this.offsetBuffer[index + 1];\n        return decodeString(this.dataBuffer, start, end);\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/fsst-dictionary/stringFsstDictionaryVector.spec.ts",
    "content": "import { describe, it, expect, beforeEach } from \"vitest\";\nimport BitVector from \"../flat/bitVector\";\nimport { StringFsstDictionaryVector } from \"./stringFsstDictionaryVector\";\n\ndescribe(\"StringFsstDictionaryVector\", () => {\n    let indexBuffer: Uint32Array;\n    let offsetBuffer: Uint32Array;\n    let dictionaryBuffer: Uint8Array;\n    let symbolOffsetBuffer: Uint32Array;\n    let symbolTableBuffer: Uint8Array;\n    let nullabilityBuffer: BitVector;\n\n    beforeEach(() => {\n        indexBuffer = new Uint32Array([0, 1, 2]);\n        offsetBuffer = new Uint32Array([0, 5, 10]);\n        dictionaryBuffer = new Uint8Array([\n            /* mock data */\n        ]);\n        symbolOffsetBuffer = new Uint32Array([0, 3, 6]);\n        symbolTableBuffer = new Uint8Array([\n            /* mock data */\n        ]);\n        nullabilityBuffer = new BitVector(new Uint8Array([0b00000001]), 2);\n    });\n\n    it(\"should create an instance of StringFsstDictionaryVector\", () => {\n        const vector = new StringFsstDictionaryVector(\n            \"testVector\",\n            indexBuffer,\n            offsetBuffer,\n            dictionaryBuffer,\n            symbolOffsetBuffer,\n            symbolTableBuffer,\n            nullabilityBuffer,\n        );\n        expect(vector).toBeInstanceOf(StringFsstDictionaryVector);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/fsst-dictionary/stringFsstDictionaryVector.ts",
    "content": "import { VariableSizeVector } from \"../variableSizeVector\";\nimport type BitVector from \"../flat/bitVector\";\nimport { decodeFsst } from \"../../decoding/fsstDecoder\";\nimport { decodeString } from \"../../decoding/decodingUtils\";\n\nexport class StringFsstDictionaryVector extends VariableSizeVector<Uint8Array, string> {\n    // TODO: extend from StringVector\n    private symbolLengthBuffer: Uint32Array;\n    private decodedDictionary: Uint8Array;\n\n    constructor(\n        name: string,\n        private readonly indexBuffer: Uint32Array,\n        offsetBuffer: Uint32Array,\n        dictionaryBuffer: Uint8Array,\n        private readonly symbolOffsetBuffer: Uint32Array,\n        private readonly symbolTableBuffer: Uint8Array,\n        nullabilityBuffer: BitVector,\n    ) {\n        super(name, offsetBuffer, dictionaryBuffer, nullabilityBuffer ?? indexBuffer.length);\n    }\n\n    protected getValueFromBuffer(index: number): string {\n        if (this.decodedDictionary == null) {\n            if (this.symbolLengthBuffer == null) {\n                // TODO: change FsstEncoder to take offsets instead of length to get rid of this conversion\n                this.symbolLengthBuffer = this.offsetToLengthBuffer(this.symbolOffsetBuffer);\n            }\n\n            this.decodedDictionary = decodeFsst(this.symbolTableBuffer, this.symbolLengthBuffer, this.dataBuffer);\n        }\n\n        const offset = this.indexBuffer[index];\n        const start = this.offsetBuffer[offset];\n        const end = this.offsetBuffer[offset + 1];\n        return decodeString(this.decodedDictionary, start, end);\n    }\n\n    // TODO: get rid of that conversion\n    private offsetToLengthBuffer(offsetBuffer: Uint32Array): Uint32Array {\n        const lengthBuffer = new Uint32Array(offsetBuffer.length - 1);\n        let previousOffset = offsetBuffer[0];\n        for (let i = 1; i < offsetBuffer.length; i++) {\n            const offset = offsetBuffer[i];\n            lengthBuffer[i - 1] = offset - previousOffset;\n            previousOffset = offset;\n        }\n\n        return lengthBuffer;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/constGeometryVector.ts",
    "content": "import { GeometryVector, type MortonSettings } from \"./geometryVector\";\nimport { GEOMETRY_TYPE } from \"./geometryType\";\nimport { VertexBufferType } from \"./vertexBufferType\";\nimport type { TopologyVector } from \"../../vector/geometry/topologyVector\";\n\nexport function createConstGeometryVector(\n    numGeometries: number,\n    geometryType: number,\n    topologyVector: TopologyVector,\n    vertexOffsets: Uint32Array | undefined,\n    vertexBuffer: Int32Array | Uint32Array,\n): ConstGeometryVector {\n    return new ConstGeometryVector(\n        numGeometries,\n        geometryType,\n        VertexBufferType.VEC_2,\n        topologyVector,\n        vertexOffsets,\n        vertexBuffer,\n    );\n}\n\nexport function createMortonEncodedConstGeometryVector(\n    numGeometries: number,\n    geometryType: number,\n    topologyVector: TopologyVector,\n    vertexOffsets: Uint32Array | undefined,\n    vertexBuffer: Int32Array | Uint32Array,\n    mortonInfo: MortonSettings,\n): ConstGeometryVector {\n    return new ConstGeometryVector(\n        numGeometries,\n        geometryType,\n        VertexBufferType.MORTON,\n        topologyVector,\n        vertexOffsets,\n        vertexBuffer,\n        mortonInfo,\n    );\n}\n\nexport class ConstGeometryVector extends GeometryVector {\n    constructor(\n        private readonly _numGeometries: number,\n        private readonly _geometryType: number,\n        vertexBufferType: VertexBufferType,\n        topologyVector: TopologyVector,\n        vertexOffsets: Uint32Array | undefined,\n        vertexBuffer: Int32Array | Uint32Array,\n        mortonSettings?: MortonSettings,\n    ) {\n        super(vertexBufferType, topologyVector, vertexOffsets, vertexBuffer, mortonSettings);\n    }\n\n    geometryType(_index: number): number {\n        return this._geometryType;\n    }\n\n    get numGeometries(): number {\n        return this._numGeometries;\n    }\n\n    containsPolygonGeometry(): boolean {\n        return this._geometryType === GEOMETRY_TYPE.POLYGON || this._geometryType === GEOMETRY_TYPE.MULTIPOLYGON;\n    }\n\n    containsSingleGeometryType(): boolean {\n        return true;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/constGpuVector.ts",
    "content": "import { GpuVector } from \"./gpuVector\";\nimport type { TopologyVector } from \"./topologyVector\";\n\nexport function createConstGpuVector(\n    numGeometries: number,\n    geometryType: number,\n    triangleOffsets: Uint32Array,\n    indexBuffer: Uint32Array,\n    vertexBuffer: Int32Array | Uint32Array,\n    topologyVector?: TopologyVector,\n): GpuVector {\n    return new ConstGpuVector(numGeometries, geometryType, triangleOffsets, indexBuffer, vertexBuffer, topologyVector);\n}\n\n//TODO: extend from GeometryVector -> make topology vector optional\nexport class ConstGpuVector extends GpuVector {\n    constructor(\n        private readonly _numGeometries: number,\n        private readonly _geometryType: number,\n        triangleOffsets: Uint32Array,\n        indexBuffer: Uint32Array,\n        vertexBuffer: Int32Array | Uint32Array,\n        topologyVector?: TopologyVector,\n    ) {\n        super(triangleOffsets, indexBuffer, vertexBuffer, topologyVector);\n    }\n\n    geometryType(_index: number): number {\n        return this._geometryType;\n    }\n\n    get numGeometries(): number {\n        return this._numGeometries;\n    }\n\n    containsSingleGeometryType(): boolean {\n        return true;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/flatGeometryVector.ts",
    "content": "import { GeometryVector, type MortonSettings } from \"./geometryVector\";\nimport { GEOMETRY_TYPE } from \"./geometryType\";\nimport { VertexBufferType } from \"./vertexBufferType\";\nimport type { TopologyVector } from \"../../vector/geometry/topologyVector\";\n\nexport function createFlatGeometryVector(\n    geometryTypes: Uint32Array,\n    topologyVector: TopologyVector,\n    vertexOffsets: Uint32Array | undefined,\n    vertexBuffer: Int32Array | Uint32Array,\n): FlatGeometryVector {\n    return new FlatGeometryVector(VertexBufferType.VEC_2, geometryTypes, topologyVector, vertexOffsets, vertexBuffer);\n}\n\nexport function createFlatGeometryVectorMortonEncoded(\n    geometryTypes: Uint32Array,\n    topologyVector: TopologyVector,\n    vertexOffsets: Uint32Array | undefined,\n    vertexBuffer: Int32Array | Uint32Array,\n    mortonInfo: MortonSettings,\n): FlatGeometryVector {\n    return new FlatGeometryVector(\n        VertexBufferType.MORTON,\n        geometryTypes,\n        topologyVector,\n        vertexOffsets,\n        vertexBuffer,\n        mortonInfo,\n    );\n}\n\nexport class FlatGeometryVector extends GeometryVector {\n    constructor(\n        vertexBufferType: VertexBufferType,\n        private readonly _geometryTypes: Uint32Array,\n        topologyVector: TopologyVector,\n        vertexOffsets: Uint32Array | undefined,\n        vertexBuffer: Int32Array | Uint32Array,\n        mortonSettings?: MortonSettings,\n    ) {\n        super(vertexBufferType, topologyVector, vertexOffsets, vertexBuffer, mortonSettings);\n    }\n\n    geometryType(index: number): number {\n        return this._geometryTypes[index];\n    }\n\n    get numGeometries(): number {\n        return this._geometryTypes.length;\n    }\n\n    containsPolygonGeometry(): boolean {\n        for (let i = 0; i < this.numGeometries; i++) {\n            if (this.geometryType(i) === GEOMETRY_TYPE.POLYGON || this.geometryType(i) === GEOMETRY_TYPE.MULTIPOLYGON) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    containsSingleGeometryType(): boolean {\n        return false;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/flatGpuVector.ts",
    "content": "import { GpuVector } from \"./gpuVector\";\nimport type { TopologyVector } from \"./topologyVector\";\n\nexport function createFlatGpuVector(\n    geometryTypes: Uint32Array,\n    triangleOffsets: Uint32Array,\n    indexBuffer: Uint32Array,\n    vertexBuffer: Int32Array | Uint32Array,\n    topologyVector?: TopologyVector,\n): GpuVector {\n    return new FlatGpuVector(geometryTypes, triangleOffsets, indexBuffer, vertexBuffer, topologyVector);\n}\n\n//TODO: extend from GeometryVector -> make topology vector optional\nexport class FlatGpuVector extends GpuVector {\n    constructor(\n        private readonly _geometryTypes: Uint32Array,\n        triangleOffsets: Uint32Array,\n        indexBuffer: Uint32Array,\n        vertexBuffer: Int32Array | Uint32Array,\n        topologyVector?: TopologyVector,\n    ) {\n        super(triangleOffsets, indexBuffer, vertexBuffer, topologyVector);\n    }\n\n    geometryType(index: number): number {\n        return this._geometryTypes[index];\n    }\n\n    get numGeometries(): number {\n        return this._geometryTypes.length;\n    }\n\n    containsSingleGeometryType(): boolean {\n        return false;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/geometryType.ts",
    "content": "export enum GEOMETRY_TYPE {\n    POINT = 0,\n    LINESTRING = 1,\n    POLYGON = 2,\n    MULTIPOINT = 3,\n    MULTILINESTRING = 4,\n    MULTIPOLYGON = 5,\n}\n\nexport enum SINGLE_PART_GEOMETRY_TYPE {\n    POINT = 0,\n    LINESTRING = 1,\n    POLYGON = 2,\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/geometryVector.ts",
    "content": "import { convertGeometryVector } from \"./geometryVectorConverter\";\nimport { decodeZOrderCurve } from \"./zOrderCurve\";\nimport type Point from \"@mapbox/point-geometry\";\nimport type { GEOMETRY_TYPE } from \"./geometryType\";\nimport type { VertexBufferType } from \"./vertexBufferType\";\nimport type { TopologyVector } from \"../../vector/geometry/topologyVector\";\n\nexport type CoordinatesArray = Array<Array<Point>>;\n\nexport type Geometry = {\n    coordinates: CoordinatesArray;\n    type: GEOMETRY_TYPE;\n};\n\nexport interface MortonSettings {\n    numBits: number;\n    coordinateShift: number;\n}\n\nexport abstract class GeometryVector {\n    protected constructor(\n        private readonly _vertexBufferType: VertexBufferType,\n        private readonly _topologyVector: TopologyVector,\n        private readonly _vertexOffsets: Uint32Array | undefined,\n        private readonly _vertexBuffer: Int32Array | Uint32Array,\n        private readonly _mortonSettings?: MortonSettings,\n    ) {}\n\n    get vertexBufferType(): VertexBufferType {\n        return this._vertexBufferType;\n    }\n\n    get topologyVector(): TopologyVector {\n        return this._topologyVector;\n    }\n\n    get vertexOffsets(): Uint32Array | undefined {\n        return this._vertexOffsets;\n    }\n\n    get vertexBuffer(): Int32Array | Uint32Array {\n        return this._vertexBuffer;\n    }\n\n    /* Allows faster access to the vertices since morton encoding is currently not used in the POC. Morton encoding\n       will be used after adapting the shader to decode the morton codes on the GPU. */\n    getSimpleEncodedVertex(index: number): [number, number] {\n        const offset = this.vertexOffsets ? this.vertexOffsets[index] * 2 : index * 2;\n        const x = this.vertexBuffer[offset];\n        const y = this.vertexBuffer[offset + 1];\n        return [x, y];\n    }\n\n    //TODO: add scaling information to the constructor\n    getVertex(index: number): [number, number] {\n        if (this.vertexOffsets && this.mortonSettings) {\n            //TODO: move decoding of the morton codes on the GPU in the vertex shader\n            const vertexOffset = this.vertexOffsets[index];\n            const mortonEncodedVertex = this.vertexBuffer[vertexOffset];\n            //TODO: improve performance -> inline calculation and move to decoding of VertexBuffer\n            const vertex = decodeZOrderCurve(\n                mortonEncodedVertex,\n                this.mortonSettings.numBits,\n                this.mortonSettings.coordinateShift,\n            );\n            return [vertex.x, vertex.y];\n        }\n\n        const offset = this.vertexOffsets ? this.vertexOffsets[index] * 2 : index * 2;\n        const x = this.vertexBuffer[offset];\n        const y = this.vertexBuffer[offset + 1];\n        return [x, y];\n    }\n\n    getGeometries(): CoordinatesArray[] {\n        return convertGeometryVector(this);\n    }\n\n    get mortonSettings(): MortonSettings | undefined {\n        return this._mortonSettings;\n    }\n\n    abstract containsPolygonGeometry(): boolean;\n\n    abstract geometryType(index: number): number;\n\n    abstract get numGeometries(): number;\n\n    abstract containsSingleGeometryType(): boolean;\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/geometryVectorConverter.spec.ts",
    "content": "import Point from \"@mapbox/point-geometry\";\nimport { describe, it, expect } from \"vitest\";\nimport { convertGeometryVector } from \"./geometryVectorConverter\";\nimport { GEOMETRY_TYPE } from \"./geometryType\";\nimport { VertexBufferType } from \"./vertexBufferType\";\nimport { encodeZOrderCurve } from \"../../encoding/zOrderCurveEncoder\";\nimport type { GeometryVector, MortonSettings } from \"./geometryVector\";\nimport {\n    encodeLineStringGeometryVector,\n    encodeLineStringGeometryVectorWithMortonEncoding,\n    encodeMultiLineStringGeometryVector,\n    encodeMultiLineStringGeometryVectorWithMortonOffsets,\n    encodeMultiLineStringGeometryVectorWithOffsets,\n    encodeMultiPointGeometryVector,\n    encodeMultiPolygonGeometryVector,\n    encodeMultiPolygonGeometryVectorWithMortonOffsets,\n    encodeMultiPolygonGeometryVectorWithOffsets,\n    encodePointGeometryVector,\n    encodePointGeometryVectorWithMortonEncoding,\n    encodePointGeometryVectorWithOffset,\n    encodePointsGeometryVector,\n    encodePolygonGeometryVector,\n    encodePolygonGeometryVectorWithMortonOffsets,\n    encodePolygonGeometryVectorWithOffsets,\n} from \"../../encoding/constGeometryVectorEncoder\";\nimport { ConstGeometryVector } from \"./constGeometryVector\";\nimport { FlatGeometryVector } from \"./flatGeometryVector\";\n\ndescribe(\"POINT – sequential vertex buffer (no vertexOffsets)\", () => {\n    it(\"creates a single point from the vertex buffer\", () => {\n        const x = 5;\n        const y = 7;\n        const gv = encodePointGeometryVector(x, y);\n        const result = convertGeometryVector(gv);\n\n        expect(result).toHaveLength(1);\n        expect(result[0]).toEqual([[new Point(x, y)]]);\n    });\n\n    it(\"creates multiple points sequentially\", () => {\n        const gv = encodePointsGeometryVector([1, 2, 3, 4]);\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(1, 2)]]);\n        expect(result[1]).toEqual([[new Point(3, 4)]]);\n    });\n});\n\ndescribe(\"POINT – VEC_2 dictionary encoded\", () => {\n    it(\"reads point via vertexOffsets with VEC_2 buffer type\", () => {\n        // vertexOffsets[0] = 1 → vertexBuffer[2], vertexBuffer[3]\n        const x = 42;\n        const y = 55;\n        const gv = encodePointGeometryVectorWithOffset(x, y);\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(x, y)]]);\n    });\n});\n\ndescribe(\"POINT – Morton dictionary encoded\", () => {\n    it(\"decodes a point via morton encoding\", () => {\n        const x = 3;\n        const y = 7;\n\n        const gv = encodePointGeometryVectorWithMortonEncoding(x, y);\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(x, y)]]);\n    });\n\n    it(\"decodes a point with a non-zero coordinateShift\", () => {\n        const x = 50;\n        const y = 80;\n        const settings: MortonSettings = { numBits: 16, coordinateShift: 100 } as MortonSettings;\n        const code = encodeZOrderCurve(x, y, settings.numBits, settings.coordinateShift);\n\n        const gv = new ConstGeometryVector(\n            1,\n            GEOMETRY_TYPE.POINT,\n            VertexBufferType.MORTON,\n            {\n                geometryOffsets: undefined,\n                partOffsets: undefined,\n                ringOffsets: undefined,\n            },\n            new Uint32Array([0]),\n            new Int32Array([code]),\n            settings,\n        );\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toEqual([[new Point(x, y)]]);\n    });\n});\n\ndescribe(\"MULTIPOINT – sequential vertex buffer\", () => {\n    it(\"creates a multi-point geometry\", () => {\n        const gv = encodeMultiPointGeometryVector([\n            [1, 2],\n            [3, 4],\n            [5, 6],\n        ]);\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(1, 2)], [new Point(3, 4)], [new Point(5, 6)]]);\n    });\n});\n\ndescribe(\"MULTIPOINT – VEC_2 dictionary encoded\", () => {\n    it(\"reads multi-point via vertexOffsets\", () => {\n        // offsets [0, 2] → vertexBuffer[0,1] and vertexBuffer[4,5]\n        const gv = new ConstGeometryVector(\n            1,\n            GEOMETRY_TYPE.MULTIPOINT,\n            VertexBufferType.VEC_2,\n            {\n                geometryOffsets: new Uint32Array([0, 2]),\n                partOffsets: undefined,\n                ringOffsets: undefined,\n            },\n            new Uint32Array([0, 2]),\n            new Int32Array([10, 20, 99, 99, 30, 40]),\n        );\n\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(10, 20)], [new Point(30, 40)]]);\n    });\n});\n\ndescribe(\"LINESTRING – sequential vertex buffer, no polygon context\", () => {\n    it(\"creates a line string from sequential vertices\", () => {\n        const gv = encodeLineStringGeometryVector([\n            [0, 0],\n            [1, 1],\n            [2, 2],\n        ]);\n        const result = convertGeometryVector(gv);\n\n        expect(result[0]).toEqual([[new Point(0, 0), new Point(1, 1), new Point(2, 2)]]);\n    });\n});\n\ndescribe(\"LINESTRING – sequential vertex buffer, polygon context (uses ringOffsets)\", () => {\n    it(\"creates a line string using ringOffsets when containsPolygon is true\", () => {\n        const gv = {\n            numGeometries: 1,\n            vertexBuffer: new Int32Array([1, 2, 3, 4]),\n            vertexOffsets: undefined,\n            topologyVector: {\n                geometryOffsets: undefined,\n                partOffsets: new Uint32Array([0, 1]),\n                ringOffsets: new Uint32Array([0, 2]),\n            },\n            geometryType: () => GEOMETRY_TYPE.LINESTRING,\n            containsPolygonGeometry: () => true,\n        } as any as GeometryVector;\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toEqual([[new Point(1, 2), new Point(3, 4)]]);\n    });\n});\n\ndescribe(\"LINESTRING – VEC_2 dictionary encoded\", () => {\n    it(\"decodes a line string via vertexOffsets VEC_2\", () => {\n        // offsets [0, 1] → vertexBuffer[0,1] and [2,3]\n        const gv = {\n            numGeometries: 1,\n            vertexBuffer: new Int32Array([5, 10, 15, 20]),\n            vertexOffsets: new Uint32Array([0, 1]),\n            vertexBufferType: VertexBufferType.VEC_2,\n            topologyVector: {\n                geometryOffsets: undefined,\n                partOffsets: new Uint32Array([0, 2]),\n                ringOffsets: undefined,\n            },\n            geometryType: () => GEOMETRY_TYPE.LINESTRING,\n            containsPolygonGeometry: () => false,\n        } as any as GeometryVector;\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toEqual([[new Point(5, 10), new Point(15, 20)]]);\n    });\n});\n\ndescribe(\"LINESTRING – Morton dictionary encoded\", () => {\n    it(\"decodes a line string via Morton encoding\", () => {\n        const gv = encodeLineStringGeometryVectorWithMortonEncoding([\n            [1, 2],\n            [3, 4],\n            [5, 6],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toEqual([[new Point(1, 2), new Point(3, 4), new Point(5, 6)]]);\n    });\n});\n\ndescribe(\"POLYGON – sequential vertex buffer, no holes\", () => {\n    it(\"creates a polygon shell that is closed\", () => {\n        const gv = encodePolygonGeometryVector([\n            [\n                [0, 0],\n                [10, 0],\n                [10, 10],\n                [0, 10],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        const shell = result[0][0];\n        expect(shell).toHaveLength(5); // 4 + closed\n        expect(shell[0]).toEqual(shell[4]);\n    });\n});\n\ndescribe(\"POLYGON – sequential vertex buffer, with hole\", () => {\n    it(\"creates a polygon with one hole\", () => {\n        const gv = encodePolygonGeometryVector([\n            [\n                [0, 0],\n                [10, 0],\n                [0, 10],\n            ],\n            [\n                [2, 2],\n                [4, 2],\n                [2, 4],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n        expect(result[0][0]).toHaveLength(4); // shell has 3 points + 1 (closing point)\n        expect(result[0][1]).toHaveLength(4); // hole has 3 points + 1 (closing point)\n    });\n});\n\ndescribe(\"POLYGON – VEC_2 dictionary encoded, no holes\", () => {\n    it(\"creates a closed polygon shell via VEC_2 vertex offsets\", () => {\n        const gv = encodePolygonGeometryVectorWithOffsets([\n            [\n                [0, 0],\n                [5, 5],\n                [10, 0],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        const shell = result[0][0];\n        expect(shell).toHaveLength(4); // closed\n        expect(shell[0]).toEqual(shell[3]);\n    });\n});\n\ndescribe(\"POLYGON – VEC_2 dictionary encoded, with hole\", () => {\n    it(\"creates a polygon with one hole via VEC_2 vertex offsets\", () => {\n        const gv = encodePolygonGeometryVectorWithOffsets([\n            [\n                [0, 0],\n                [10, 0],\n                [0, 10],\n            ],\n            [\n                [2, 2],\n                [4, 2],\n                [2, 4],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n        expect(result[0][0]).toHaveLength(4); // shell has 3 points + 1 (closing point)\n        expect(result[0][1]).toHaveLength(4); // hole has 3 points + 1 (closing point)\n    });\n});\n\ndescribe(\"POLYGON – Morton dictionary encoded, no holes\", () => {\n    it(\"creates a closed polygon shell via Morton encoding\", () => {\n        const gv = encodePolygonGeometryVectorWithMortonOffsets([\n            [\n                [0, 0],\n                [5, 0],\n                [0, 5],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        const shell = result[0][0];\n        expect(shell).toHaveLength(4);\n        expect(shell[0]).toEqual(new Point(0, 0));\n        expect(shell[1]).toEqual(new Point(5, 0));\n        expect(shell[2]).toEqual(new Point(0, 5));\n        expect(shell[3]).toEqual(shell[0]); // closed\n    });\n});\n\ndescribe(\"POLYGON – Morton dictionary encoded, with hole\", () => {\n    it(\"creates a polygon with one hole via Morton encoding\", () => {\n        const gv = encodePolygonGeometryVectorWithMortonOffsets([\n            [\n                [0, 0],\n                [10, 0],\n                [0, 10],\n            ],\n            [\n                [1, 1],\n                [3, 1],\n                [1, 3],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n        expect(result[0][0]).toHaveLength(4); // shell has 3 points + 1 (closing point)\n        expect(result[0][1]).toHaveLength(4); // hole has 3 points + 1 (closing point)\n    });\n});\n\ndescribe(\"MULTILINESTRING – sequential vertex buffer, no polygon context\", () => {\n    it(\"creates a multi-line string with two line strings\", () => {\n        const gv = encodeMultiLineStringGeometryVector([\n            [\n                [0, 0],\n                [1, 1],\n            ],\n            [\n                [2, 2],\n                [3, 3],\n                [4, 4],\n            ],\n            [\n                [5, 5],\n                [6, 6],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(3);\n        expect(result[0][0]).toEqual([new Point(0, 0), new Point(1, 1)]);\n        expect(result[0][1]).toEqual([new Point(2, 2), new Point(3, 3), new Point(4, 4)]);\n        expect(result[0][2]).toEqual([new Point(5, 5), new Point(6, 6)]);\n    });\n});\n\ndescribe(\"MULTILINESTRING – VEC_2 dictionary encoded\", () => {\n    it(\"decodes multi-line string via vertexOffsets VEC_2\", () => {\n        const gv = encodeMultiLineStringGeometryVectorWithOffsets([\n            [\n                [0, 1],\n                [2, 3],\n            ],\n            [\n                [4, 5],\n                [6, 7],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n        expect(result[0][0]).toEqual([new Point(0, 1), new Point(2, 3)]);\n        expect(result[0][1]).toEqual([new Point(4, 5), new Point(6, 7)]);\n    });\n});\n\ndescribe(\"MULTILINESTRING – Morton dictionary encoded\", () => {\n    it(\"decodes multi-line string via Morton encoding\", () => {\n        const gv = encodeMultiLineStringGeometryVectorWithMortonOffsets([\n            [\n                [1, 2],\n                [3, 4],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0][0]).toEqual([new Point(1, 2), new Point(3, 4)]);\n    });\n});\n\ndescribe(\"MULTIPOLYGON – sequential vertex buffer, no holes\", () => {\n    it(\"creates a multi-polygon with two triangular polygons\", () => {\n        const gv = encodeMultiPolygonGeometryVector([\n            [\n                [\n                    [0, 0],\n                    [1, 0],\n                    [0, 1],\n                ],\n            ],\n            [\n                [\n                    [5, 5],\n                    [6, 5],\n                    [5, 6],\n                ],\n            ],\n            [\n                [\n                    [10, 10],\n                    [10, 0],\n                    [0, 10],\n                ],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(3);\n        expect(result[0][0]).toHaveLength(4); // closed shell\n        expect(result[0][1]).toHaveLength(4); // closed shell\n        expect(result[0][2]).toHaveLength(4); // closed shell\n    });\n});\n\ndescribe(\"MULTIPOLYGON – sequential vertex buffer, with holes\", () => {\n    it(\"creates a polygon that has a hole\", () => {\n        const gv = encodeMultiPolygonGeometryVector([\n            [\n                [\n                    [0, 0],\n                    [10, 0],\n                    [0, 10],\n                ],\n                [\n                    [1, 1],\n                    [3, 1],\n                    [1, 3],\n                ],\n            ],\n            [\n                [\n                    [5, 5],\n                    [25, 5],\n                    [5, 25],\n                ],\n                [\n                    [6, 6],\n                    [7, 6],\n                    [6, 7],\n                ],\n            ],\n            [\n                [\n                    [10, 10],\n                    [10, 0],\n                    [0, 10],\n                ],\n            ],\n        ]);\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(5);\n        expect(result[0][0]).toHaveLength(4);\n        expect(result[0][1]).toHaveLength(4);\n        expect(result[0][2]).toHaveLength(4);\n        expect(result[0][3]).toHaveLength(4);\n        expect(result[0][4]).toHaveLength(4);\n    });\n});\n\ndescribe(\"MULTIPOLYGON – VEC_2 dictionary encoded, no holes\", () => {\n    it(\"decodes multi-polygon via vertexOffsets VEC_2\", () => {\n        const gv = encodeMultiPolygonGeometryVectorWithOffsets([\n            [\n                [\n                    [0, 0],\n                    [2, 0],\n                    [0, 2],\n                ],\n            ],\n            [\n                [\n                    [5, 5],\n                    [7, 5],\n                    [5, 7],\n                ],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n        expect(result[0][0]).toHaveLength(4); // closed\n    });\n});\n\ndescribe(\"MULTIPOLYGON – VEC_2 dictionary encoded, with holes\", () => {\n    it(\"creates a multi-polygon with hole via VEC_2\", () => {\n        const gv = encodeMultiPolygonGeometryVectorWithOffsets([\n            [\n                [\n                    [0, 0],\n                    [10, 0],\n                    [0, 10],\n                ],\n                [\n                    [1, 1],\n                    [3, 1],\n                    [1, 3],\n                ],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n    });\n});\n\ndescribe(\"MULTIPOLYGON – Morton dictionary encoded, no holes\", () => {\n    it(\"decodes multi-polygon shells via Morton encoding\", () => {\n        const gv = encodeMultiPolygonGeometryVectorWithMortonOffsets([\n            [\n                [\n                    [0, 0],\n                    [1, 0],\n                    [0, 1],\n                ],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        const shell = result[0][0];\n        expect(shell).toHaveLength(4); // closed\n        expect(shell[0]).toEqual(new Point(0, 0));\n        expect(shell[3]).toEqual(shell[0]);\n    });\n});\n\ndescribe(\"MULTIPOLYGON – Morton dictionary encoded, with holes\", () => {\n    it(\"creates a multi-polygon with hole via Morton encoding\", () => {\n        const gv = encodeMultiPolygonGeometryVectorWithMortonOffsets([\n            [\n                [\n                    [0, 0],\n                    [10, 0],\n                    [0, 10],\n                ],\n                [\n                    [1, 1],\n                    [3, 1],\n                    [1, 3],\n                ],\n            ],\n        ]);\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toHaveLength(2);\n    });\n});\n\ndescribe(\"Vector with multiple geometries of different types\", () => {\n    it(\"converts geometries of different types in the same vector\", () => {\n        const pointGv = encodePointGeometryVector(1, 2);\n        const multiPointGv = encodeMultiPointGeometryVector([\n            [3, 4],\n            [5, 6],\n        ]);\n        const lineStringGv = encodeLineStringGeometryVector([\n            [7, 8],\n            [9, 10],\n        ]);\n        const polygonGv = encodePolygonGeometryVector([\n            [\n                [11, 11],\n                [11, 12],\n                [12, 11],\n            ],\n        ]);\n        const multiLineStringGv = encodeMultiLineStringGeometryVector([\n            [\n                [13, 14],\n                [15, 16],\n            ],\n        ]);\n\n        const gv = new FlatGeometryVector(\n            VertexBufferType.VEC_2,\n            new Uint32Array([\n                GEOMETRY_TYPE.POINT,\n                GEOMETRY_TYPE.MULTIPOINT,\n                GEOMETRY_TYPE.LINESTRING,\n                GEOMETRY_TYPE.POLYGON,\n                GEOMETRY_TYPE.MULTILINESTRING,\n            ]),\n            {\n                geometryOffsets: new Uint32Array([0, 1, 3, 5, 8, 9]),\n                partOffsets: new Uint32Array([0, 1, 2, 3, 4, 5, 6]),\n                ringOffsets: new Uint32Array([0, 1, 2, 3, 5, 8, 10]),\n            },\n            undefined,\n            new Int32Array([\n                ...pointGv.vertexBuffer,\n                ...multiPointGv.vertexBuffer,\n                ...lineStringGv.vertexBuffer,\n                ...polygonGv.vertexBuffer,\n                ...multiLineStringGv.vertexBuffer,\n            ]),\n        );\n\n        const result = convertGeometryVector(gv);\n        expect(result).toHaveLength(5);\n        expect(result[0]).toEqual([[new Point(1, 2)]]);\n        expect(result[1]).toEqual([[new Point(3, 4)], [new Point(5, 6)]]);\n        expect(result[2]).toEqual([[new Point(7, 8), new Point(9, 10)]]);\n        expect(result[3]).toEqual([\n            [\n                new Point(11, 11),\n                new Point(11, 12),\n                new Point(12, 11),\n                new Point(11, 11), // closed\n            ],\n        ]);\n        expect(result[4]).toEqual([[new Point(13, 14), new Point(15, 16)]]);\n    });\n});\n\ndescribe(\"Error handling\", () => {\n    it(\"throws on unsupported geometry type\", () => {\n        const gv = {\n            numGeometries: 1,\n            topologyVector: {},\n            geometryType: () => 999 as unknown as GEOMETRY_TYPE,\n            containsPolygonGeometry: () => false,\n        } as unknown as GeometryVector;\n\n        expect(() => convertGeometryVector(gv)).toThrowError(\"The specified geometry type is currently not supported.\");\n    });\n});\n\ndescribe(\"Edge cases\", () => {\n    it(\"returns an empty array when numGeometries is 0\", () => {\n        const gv = {\n            numGeometries: 0,\n            topologyVector: {},\n            containsPolygonGeometry: () => false,\n        } as unknown as GeometryVector;\n        expect(convertGeometryVector(gv)).toHaveLength(0);\n    });\n\n    it(\"treats vertexOffsets with length 0 as absent (sequential buffer path)\", () => {\n        const gv = new ConstGeometryVector(\n            1,\n            GEOMETRY_TYPE.POINT,\n            VertexBufferType.VEC_2,\n            {\n                geometryOffsets: undefined,\n                partOffsets: undefined,\n                ringOffsets: undefined,\n            },\n            new Uint32Array([]), // length === 0 → same as undefined\n            new Int32Array([3, 9]),\n        );\n\n        const result = convertGeometryVector(gv);\n        expect(result[0]).toEqual([[new Point(3, 9)]]);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/geometry/geometryVectorConverter.ts",
    "content": "import type { GeometryVector, MortonSettings, CoordinatesArray } from \"./geometryVector\";\nimport { decodeZOrderCurve } from \"./zOrderCurve\";\nimport { GEOMETRY_TYPE } from \"./geometryType\";\nimport { VertexBufferType } from \"./vertexBufferType\";\nimport Point from \"@mapbox/point-geometry\";\n\nexport function convertGeometryVector(geometryVector: GeometryVector): CoordinatesArray[] {\n    const geometries: CoordinatesArray[] = new Array(geometryVector.numGeometries);\n    let partOffsetCounter = 1;\n    let ringOffsetsCounter = 1;\n    let geometryOffsetsCounter = 1;\n    let geometryCounter = 0;\n    let vertexBufferOffset = 0;\n    let vertexOffsetsOffset = 0;\n\n    const mortonSettings = geometryVector.mortonSettings;\n    const topologyVector = geometryVector.topologyVector;\n    const geometryOffsets = topologyVector.geometryOffsets;\n    const partOffsets = topologyVector.partOffsets;\n    const ringOffsets = topologyVector.ringOffsets;\n    const vertexOffsets = geometryVector.vertexOffsets;\n    const nonOffset = !vertexOffsets || vertexOffsets.length === 0;\n\n    const containsPolygon = geometryVector.containsPolygonGeometry();\n    const vertexBuffer = geometryVector.vertexBuffer;\n\n    for (let i = 0; i < geometryVector.numGeometries; i++) {\n        const geometryType = geometryVector.geometryType(i);\n        switch (geometryType) {\n            case GEOMETRY_TYPE.POINT:\n                {\n                    let x: number;\n                    let y: number;\n                    if (nonOffset) {\n                        x = vertexBuffer[vertexBufferOffset++];\n                        y = vertexBuffer[vertexBufferOffset++];\n                    } else if (geometryVector.vertexBufferType === VertexBufferType.MORTON) {\n                        const offset = vertexOffsets[vertexOffsetsOffset++];\n                        const mortonCode = vertexBuffer[offset];\n                        const vertex = decodeZOrderCurve(\n                            mortonCode,\n                            mortonSettings.numBits,\n                            mortonSettings.coordinateShift,\n                        );\n                        x = vertex.x;\n                        y = vertex.y;\n                    } else {\n                        const offset = vertexOffsets[vertexOffsetsOffset++] * 2;\n                        x = vertexBuffer[offset];\n                        y = vertexBuffer[offset + 1];\n                    }\n                    geometries[geometryCounter++] = [[new Point(x, y)]];\n                    if (geometryOffsets) geometryOffsetsCounter++;\n                    if (partOffsets) partOffsetCounter++;\n                    if (ringOffsets) ringOffsetsCounter++;\n                }\n                break;\n            case GEOMETRY_TYPE.MULTIPOINT:\n                {\n                    const numPoints =\n                        geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];\n                    geometryOffsetsCounter++;\n                    const points: Point[] = new Array(numPoints);\n                    if (nonOffset) {\n                        for (let j = 0; j < numPoints; j++) {\n                            const x = vertexBuffer[vertexBufferOffset++];\n                            const y = vertexBuffer[vertexBufferOffset++];\n                            points[j] = new Point(x, y);\n                        }\n                    } else {\n                        for (let j = 0; j < numPoints; j++) {\n                            const offset = vertexOffsets[vertexOffsetsOffset++] * 2;\n                            const x = vertexBuffer[offset];\n                            const y = vertexBuffer[offset + 1];\n                            points[j] = new Point(x, y);\n                        }\n                    }\n                    geometries[geometryCounter++] = points.map((point) => [point]);\n                    // MULTIPOINT must increment offset counters like POINT does\n                    partOffsetCounter += numPoints;\n                    ringOffsetsCounter += numPoints;\n                }\n                break;\n            case GEOMETRY_TYPE.LINESTRING:\n                {\n                    let numVertices: number;\n                    if (containsPolygon) {\n                        numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                        ringOffsetsCounter++;\n                    } else {\n                        numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                    }\n                    partOffsetCounter++;\n\n                    let vertices: Point[];\n                    if (nonOffset) {\n                        vertices = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, false);\n                        vertexBufferOffset += numVertices * 2;\n                    } else {\n                        vertices = decodeDictionaryEncodedLineStringOrRing(\n                            geometryVector.vertexBufferType,\n                            vertexBuffer,\n                            vertexOffsets,\n                            vertexOffsetsOffset,\n                            numVertices,\n                            false,\n                            mortonSettings,\n                        );\n                        vertexOffsetsOffset += numVertices;\n                    }\n\n                    geometries[geometryCounter++] = [vertices];\n\n                    if (geometryOffsets) geometryOffsetsCounter++;\n                }\n                break;\n            case GEOMETRY_TYPE.POLYGON:\n                {\n                    const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                    partOffsetCounter++;\n                    const rings: CoordinatesArray = new Array(numRings - 1);\n                    let shell: Point[];\n                    let numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                    ringOffsetsCounter++;\n\n                    if (nonOffset) {\n                        shell = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);\n                        vertexBufferOffset += numVertices * 2;\n                        for (let j = 0; j < rings.length; j++) {\n                            numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                            rings[j] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);\n                            vertexBufferOffset += numVertices * 2;\n                        }\n                    } else {\n                        shell = decodeDictionaryEncodedLineStringOrRing(\n                            geometryVector.vertexBufferType,\n                            vertexBuffer,\n                            vertexOffsets,\n                            vertexOffsetsOffset,\n                            numVertices,\n                            true,\n                            mortonSettings,\n                        );\n                        vertexOffsetsOffset += numVertices;\n                        for (let j = 0; j < rings.length; j++) {\n                            numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                            rings[j] = decodeDictionaryEncodedLineStringOrRing(\n                                geometryVector.vertexBufferType,\n                                vertexBuffer,\n                                vertexOffsets,\n                                vertexOffsetsOffset,\n                                numVertices,\n                                true,\n                                mortonSettings,\n                            );\n                            vertexOffsetsOffset += numVertices;\n                        }\n                    }\n                    geometries[geometryCounter++] = [shell].concat(rings);\n                    if (geometryOffsets) geometryOffsetsCounter++;\n                }\n                break;\n            case GEOMETRY_TYPE.MULTILINESTRING:\n                {\n                    const numLineStrings =\n                        geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];\n                    geometryOffsetsCounter++;\n                    const lineStrings: CoordinatesArray = new Array(numLineStrings);\n                    for (let j = 0; j < numLineStrings; j++) {\n                        let numVertices: number;\n                        if (containsPolygon) {\n                            numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                        } else {\n                            numVertices = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        }\n                        partOffsetCounter++;\n                        if (nonOffset) {\n                            lineStrings[j] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, false);\n                            vertexBufferOffset += numVertices * 2;\n                        } else {\n                            const vertices = decodeDictionaryEncodedLineStringOrRing(\n                                geometryVector.vertexBufferType,\n                                vertexBuffer,\n                                vertexOffsets,\n                                vertexOffsetsOffset,\n                                numVertices,\n                                false,\n                                mortonSettings,\n                            );\n                            lineStrings[j] = vertices;\n                            vertexOffsetsOffset += numVertices;\n                        }\n                    }\n                    geometries[geometryCounter++] = lineStrings;\n                }\n                break;\n            case GEOMETRY_TYPE.MULTIPOLYGON:\n                {\n                    const numPolygons =\n                        geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];\n                    geometryOffsetsCounter++;\n                    const polygons: CoordinatesArray[] = new Array(numPolygons);\n                    for (let j = 0; j < numPolygons; j++) {\n                        const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        partOffsetCounter++;\n                        let shell: Point[];\n                        const rings: CoordinatesArray = new Array(numRings - 1);\n                        const numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                        ringOffsetsCounter++;\n                        if (nonOffset) {\n                            shell = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numVertices, true);\n                            vertexBufferOffset += numVertices * 2;\n                        } else {\n                            shell = decodeDictionaryEncodedLineStringOrRing(\n                                geometryVector.vertexBufferType,\n                                vertexBuffer,\n                                vertexOffsets,\n                                vertexOffsetsOffset,\n                                numVertices,\n                                true,\n                                mortonSettings,\n                            );\n                            vertexOffsetsOffset += numVertices;\n                        }\n                        for (let k = 0; k < rings.length; k++) {\n                            const numRingVertices =\n                                ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                            if (nonOffset) {\n                                rings[k] = getLineStringOrRing(vertexBuffer, vertexBufferOffset, numRingVertices, true);\n                                vertexBufferOffset += numRingVertices * 2;\n                            } else {\n                                rings[k] = decodeDictionaryEncodedLineStringOrRing(\n                                    geometryVector.vertexBufferType,\n                                    vertexBuffer,\n                                    vertexOffsets,\n                                    vertexOffsetsOffset,\n                                    numRingVertices,\n                                    true,\n                                    mortonSettings,\n                                );\n                                vertexOffsetsOffset += numRingVertices;\n                            }\n                        }\n                        polygons[j] = [shell].concat(rings);\n                    }\n                    geometries[geometryCounter++] = polygons.flat();\n                }\n                break;\n            default:\n                throw new Error(\"The specified geometry type is currently not supported.\");\n        }\n    }\n\n    return geometries;\n}\n\nfunction decodeDictionaryEncodedLineStringOrRing(\n    vertexBufferType: VertexBufferType,\n    vertexBuffer: Int32Array | Uint32Array,\n    vertexOffsets: Uint32Array,\n    vertexOffset: number,\n    numVertices: number,\n    closeLineString: boolean,\n    mortonSettings: MortonSettings,\n): Point[] {\n    if (vertexBufferType === VertexBufferType.MORTON) {\n        return decodeMortonDictionaryEncodedLineString(\n            vertexBuffer,\n            vertexOffsets,\n            vertexOffset,\n            numVertices,\n            closeLineString,\n            mortonSettings,\n        );\n    } else {\n        return decodeDictionaryEncodedLineString(\n            vertexBuffer,\n            vertexOffsets,\n            vertexOffset,\n            numVertices,\n            closeLineString,\n        );\n    }\n}\n\nfunction getLineStringOrRing(\n    vertexBuffer: Int32Array | Uint32Array,\n    startIndex: number,\n    numVertices: number,\n    closeLineString: boolean,\n): Point[] {\n    const vertices: Point[] = new Array(closeLineString ? numVertices + 1 : numVertices);\n    for (let i = 0; i < numVertices * 2; i += 2) {\n        const x = vertexBuffer[startIndex + i];\n        const y = vertexBuffer[startIndex + i + 1];\n        vertices[i / 2] = new Point(x, y);\n    }\n\n    if (closeLineString) {\n        vertices[vertices.length - 1] = vertices[0];\n    }\n    return vertices;\n}\n\nfunction decodeDictionaryEncodedLineString(\n    vertexBuffer: Int32Array | Uint32Array,\n    vertexOffsets: Uint32Array,\n    vertexOffset: number,\n    numVertices: number,\n    closeLineString: boolean,\n): Point[] {\n    const vertices: Point[] = new Array(closeLineString ? numVertices + 1 : numVertices);\n    for (let i = 0; i < numVertices * 2; i += 2) {\n        const offset = vertexOffsets[vertexOffset + i / 2] * 2;\n        const x = vertexBuffer[offset];\n        const y = vertexBuffer[offset + 1];\n        vertices[i / 2] = new Point(x, y);\n    }\n\n    if (closeLineString) {\n        vertices[vertices.length - 1] = vertices[0];\n    }\n    return vertices;\n}\n\nfunction decodeMortonDictionaryEncodedLineString(\n    vertexBuffer: Int32Array | Uint32Array,\n    vertexOffsets: Uint32Array,\n    vertexOffset: number,\n    numVertices: number,\n    closeLineString: boolean,\n    mortonSettings: MortonSettings,\n): Point[] {\n    const vertices: Point[] = new Array(closeLineString ? numVertices + 1 : numVertices);\n    for (let i = 0; i < numVertices; i++) {\n        const offset = vertexOffsets[vertexOffset + i];\n        const mortonEncodedVertex = vertexBuffer[offset];\n        const vertex = decodeZOrderCurve(mortonEncodedVertex, mortonSettings.numBits, mortonSettings.coordinateShift);\n        vertices[i] = new Point(vertex.x, vertex.y);\n    }\n    if (closeLineString) {\n        vertices[vertices.length - 1] = vertices[0];\n    }\n\n    return vertices;\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/gpuVector.ts",
    "content": "import Point from \"@mapbox/point-geometry\";\nimport { GEOMETRY_TYPE } from \"./geometryType\";\nimport type { CoordinatesArray } from \"./geometryVector\";\nimport type { TopologyVector } from \"./topologyVector\";\n\nexport abstract class GpuVector implements Iterable<CoordinatesArray> {\n    protected constructor(\n        private readonly _triangleOffsets: Uint32Array,\n        private readonly _indexBuffer: Uint32Array,\n        private readonly _vertexBuffer: Int32Array | Uint32Array,\n        private readonly _topologyVector?: TopologyVector,\n    ) {}\n\n    abstract geometryType(index: number): number;\n\n    abstract get numGeometries(): number;\n\n    abstract containsSingleGeometryType(): boolean;\n\n    get triangleOffsets(): Uint32Array {\n        return this._triangleOffsets;\n    }\n\n    get indexBuffer(): Uint32Array {\n        return this._indexBuffer;\n    }\n\n    get vertexBuffer(): Int32Array | Uint32Array {\n        return this._vertexBuffer;\n    }\n\n    get topologyVector(): TopologyVector | undefined {\n        return this._topologyVector;\n    }\n\n    /**\n     * Returns geometries as coordinate arrays by extracting polygon outlines from topology.\n     * The vertexBuffer contains the outline vertices, separate from the tessellated triangles.\n     */\n    getGeometries(): CoordinatesArray[] {\n        if (!this._topologyVector) {\n            throw new Error(\"Cannot convert GpuVector to coordinates without topology information\");\n        }\n\n        const geometries: CoordinatesArray[] = new Array(this.numGeometries);\n        const topology = this._topologyVector;\n        const partOffsets = topology.partOffsets;\n        const ringOffsets = topology.ringOffsets;\n        const geometryOffsets = topology.geometryOffsets;\n\n        // Use counters to track position in offset arrays (like Java implementation)\n        let vertexBufferOffset = 0;\n        let partOffsetCounter = 1;\n        let ringOffsetsCounter = 1;\n        let geometryOffsetsCounter = 1;\n\n        for (let i = 0; i < this.numGeometries; i++) {\n            const geometryType = this.geometryType(i);\n\n            switch (geometryType) {\n                case GEOMETRY_TYPE.POLYGON:\n                    {\n                        // Get number of rings for this polygon\n                        const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                        partOffsetCounter++;\n                        const rings: Point[][] = [];\n\n                        for (let j = 0; j < numRings; j++) {\n                            // Get number of vertices in this ring\n                            const numVertices = ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                            ringOffsetsCounter++;\n                            const ring: Point[] = [];\n\n                            for (let k = 0; k < numVertices; k++) {\n                                const x = this._vertexBuffer[vertexBufferOffset++];\n                                const y = this._vertexBuffer[vertexBufferOffset++];\n                                ring.push(new Point(x, y));\n                            }\n                            // Close the ring by duplicating the first vertex (MVT format requirement)\n                            if (ring.length > 0) {\n                                ring.push(ring[0]);\n                            }\n                            rings.push(ring);\n                        }\n\n                        geometries[i] = rings;\n                        if (geometryOffsets) geometryOffsetsCounter++;\n                    }\n                    break;\n                case GEOMETRY_TYPE.MULTIPOLYGON:\n                    {\n                        // Get number of polygons in this multipolygon\n                        const numPolygons =\n                            geometryOffsets[geometryOffsetsCounter] - geometryOffsets[geometryOffsetsCounter - 1];\n                        geometryOffsetsCounter++;\n                        const allRings: Point[][] = [];\n\n                        for (let p = 0; p < numPolygons; p++) {\n                            // Get number of rings in this polygon\n                            const numRings = partOffsets[partOffsetCounter] - partOffsets[partOffsetCounter - 1];\n                            partOffsetCounter++;\n\n                            for (let j = 0; j < numRings; j++) {\n                                // Get number of vertices in this ring\n                                const numVertices =\n                                    ringOffsets[ringOffsetsCounter] - ringOffsets[ringOffsetsCounter - 1];\n                                ringOffsetsCounter++;\n                                const ring: Point[] = [];\n\n                                for (let k = 0; k < numVertices; k++) {\n                                    const x = this._vertexBuffer[vertexBufferOffset++];\n                                    const y = this._vertexBuffer[vertexBufferOffset++];\n                                    ring.push(new Point(x, y));\n                                }\n                                // Close the ring by duplicating the first vertex (MVT format requirement)\n                                if (ring.length > 0) {\n                                    ring.push(ring[0]);\n                                }\n                                allRings.push(ring);\n                            }\n                        }\n\n                        geometries[i] = allRings;\n                    }\n                    break;\n            }\n        }\n        return geometries;\n    }\n\n    [Symbol.iterator](): Iterator<CoordinatesArray> {\n        /*for(let i = 1; i < this.triangleOffsets.length; i++) {\n           const numTriangles = this.triangleOffsets[i] - this.triangleOffsets[i-1];\n           const startIndex = this.triangleOffsets[i-1] * 3;\n           const endIndex = this.triangleOffsets[i] * 3;\n       }\n\n        while (index < this.numGeometries) {\n            yield geometries[index++];\n        }*/\n\n        //throw new Error(\"Iterator on a GpuVector is not implemented yet.\");\n        return null;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/topologyVector.ts",
    "content": "export type TopologyVector = {\n    readonly geometryOffsets?: Uint32Array;\n    readonly partOffsets?: Uint32Array;\n    readonly ringOffsets?: Uint32Array;\n};\n"
  },
  {
    "path": "ts/src/vector/geometry/vertexBufferType.ts",
    "content": "export enum VertexBufferType {\n    MORTON = 0,\n    VEC_2 = 1,\n    VEC_3 = 2,\n}\n"
  },
  {
    "path": "ts/src/vector/geometry/zOrderCurve.spec.ts",
    "content": "import { describe, it, expect } from \"vitest\";\nimport { decodeZOrderCurve } from \"./zOrderCurve\";\nimport { encodeZOrderCurve } from \"../../encoding/zOrderCurveEncoder\";\n\ndescribe(\"zOrderCurve\", () => {\n    it(\"should encode and decode z-order curve\", () => {\n        const x = 3358;\n        const y = 4130;\n        const numBits = 13;\n        const coordinateShift = 0;\n\n        const encoded = encodeZOrderCurve(x, y, numBits, coordinateShift);\n        expect(encoded).toBe(38865244);\n\n        const decoded = decodeZOrderCurve(encoded, numBits, coordinateShift);\n        expect(decoded).toEqual({ x, y });\n    });\n\n    it(\"should handle coordinate shift\", () => {\n        const x = -50;\n        const y = 30;\n        const numBits = 8;\n        const coordinateShift = 100;\n\n        const encoded = encodeZOrderCurve(x, y, numBits, coordinateShift);\n        const decoded = decodeZOrderCurve(encoded, numBits, coordinateShift);\n        expect(decoded).toEqual({ x, y });\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/geometry/zOrderCurve.ts",
    "content": "export function decodeZOrderCurve(\n    mortonCode: number,\n    numBits: number,\n    coordinateShift: number,\n): { x: number; y: number } {\n    const x = decodeMorton(mortonCode, numBits) - coordinateShift;\n    const y = decodeMorton(mortonCode >> 1, numBits) - coordinateShift;\n    return { x, y };\n}\n\nfunction decodeMorton(code: number, numBits: number): number {\n    let coordinate = 0;\n    for (let i = 0; i < numBits; i++) {\n        coordinate |= (code & (1 << (2 * i))) >> i;\n    }\n    return coordinate;\n}\n"
  },
  {
    "path": "ts/src/vector/idVector.ts",
    "content": "import type { Int32FlatVector } from \"./flat/int32FlatVector\";\nimport type { Int64FlatVector } from \"./flat/int64FlatVector\";\nimport type { Int32ConstVector } from \"./constant/int32ConstVector\";\nimport type { Int64ConstVector } from \"./constant/int64ConstVector\";\nimport type { Int32SequenceVector } from \"./sequence/int32SequenceVector\";\nimport type { Int64SequenceVector } from \"./sequence/int64SequenceVector\";\nimport type { DoubleFlatVector } from \"./flat/doubleFlatVector\";\n\nexport type IdVector =\n    | Int32FlatVector\n    | Int64FlatVector\n    | DoubleFlatVector\n    | Int32SequenceVector\n    | Int64SequenceVector\n    | Int32ConstVector\n    | Int64ConstVector;\n"
  },
  {
    "path": "ts/src/vector/sequence/int32SequenceVector.ts",
    "content": "import { SequenceVector } from \"./sequenceVector\";\n\nexport class Int32SequenceVector extends SequenceVector<Int32Array, number> {\n    public constructor(name: string, baseValue: number, delta: number, size: number) {\n        super(name, Int32Array.of(baseValue), delta, size);\n    }\n    protected getValueFromBuffer(index: number): number {\n        return this.dataBuffer[0] + index * this.delta;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/sequence/int64SequenceVector.spec.ts",
    "content": "import { describe, expect, it } from \"vitest\";\nimport { Int64SequenceVector } from \"./int64SequenceVector\";\n\ndescribe(\"Int64SequenceVector\", () => {\n    it(\"should generate bigint sequence values\", () => {\n        const vec = new Int64SequenceVector(\"test\", 10n, 5n, 5); // 10n, 15n, 20n, 25n, 30n\n        expect(vec.getValue(0)).toBe(10n);\n        expect(vec.getValue(2)).toBe(20n);\n        expect(vec.getValue(4)).toBe(30n);\n    });\n});\n"
  },
  {
    "path": "ts/src/vector/sequence/int64SequenceVector.ts",
    "content": "import { SequenceVector } from \"./sequenceVector\";\n\nexport class Int64SequenceVector extends SequenceVector<BigInt64Array, bigint> {\n    public constructor(name: string, baseValue: bigint, delta: bigint, size: number) {\n        super(name, BigInt64Array.of(baseValue), delta, size);\n    }\n\n    protected getValueFromBuffer(index: number): bigint {\n        return this.dataBuffer[0] + BigInt(index) * this.delta;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/sequence/sequenceVector.ts",
    "content": "import Vector from \"../vector\";\n\nexport abstract class SequenceVector<T extends ArrayBufferView, K> extends Vector<T, K> {\n    protected readonly delta: K;\n\n    protected constructor(name: string, baseValueBuffer: T, delta: K, size: number) {\n        super(name, baseValueBuffer, size);\n        this.delta = delta;\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/variableSizeVector.ts",
    "content": "import type BitVector from \"./flat/bitVector\";\nimport Vector from \"./vector\";\n\nexport abstract class VariableSizeVector<T extends ArrayBufferView, K> extends Vector<T, K> {\n    protected constructor(\n        name: string,\n        protected offsetBuffer: Uint32Array,\n        dataBuffer: T,\n        sizeOrNullabilityBuffer: number | BitVector,\n    ) {\n        super(name, dataBuffer, sizeOrNullabilityBuffer);\n    }\n}\n"
  },
  {
    "path": "ts/src/vector/vector.ts",
    "content": "import type BitVector from \"./flat/bitVector\";\n\nexport default abstract class Vector<T extends ArrayBufferView = ArrayBufferView, K = unknown> {\n    protected nullabilityBuffer: BitVector | null;\n    protected _size: number;\n\n    constructor(\n        private readonly _name: string,\n        protected readonly dataBuffer: T,\n        sizeOrNullabilityBuffer: number | BitVector,\n    ) {\n        if (typeof sizeOrNullabilityBuffer === \"number\") {\n            this._size = sizeOrNullabilityBuffer;\n        } else {\n            this.nullabilityBuffer = sizeOrNullabilityBuffer;\n            this._size = sizeOrNullabilityBuffer.size();\n        }\n    }\n\n    getValue(index: number): K | null {\n        return this.nullabilityBuffer && !this.nullabilityBuffer.get(index) ? null : this.getValueFromBuffer(index);\n    }\n\n    has(index: number): boolean {\n        return this.nullabilityBuffer?.get(index) || !this.nullabilityBuffer;\n    }\n\n    get name(): string {\n        return this._name;\n    }\n\n    get size(): number {\n        return this._size;\n    }\n\n    protected abstract getValueFromBuffer(index: number): K;\n}\n"
  },
  {
    "path": "ts/src/vector/vectorType.ts",
    "content": "export enum VectorType {\n    FLAT = 0,\n    CONST = 1,\n    SEQUENCE = 2,\n    DICTIONARY = 3,\n    FSST_DICTIONARY = 4,\n}\n"
  },
  {
    "path": "ts/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"outDir\": \"./dist\",\n    \"resolveJsonModule\": true,\n    \"module\": \"es2022\",\n    \"declaration\": true,\n    \"sourceMap\": true,\n    \"inlineSources\": true,\n    \"moduleResolution\": \"bundler\",\n    \"esModuleInterop\": true,\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    \"skipLibCheck\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"types\": [\n      \"node\"\n    ]\n  },\n  \"include\": [\n    \"src/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \"**/*.spec.ts\",\n  ]\n}\n"
  },
  {
    "path": "ts/tsconfig.lint.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": true\n  },\n  \"include\": [\n    \"src/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  }
]